commit 8b068bdb10c9d7575448475b70f97e2e48106186 Author: darren Date: Thu Jul 16 22:16:45 2026 +0700 init push diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..ae26774 --- /dev/null +++ b/.air.toml @@ -0,0 +1,52 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./tmp/api" + cmd = "go build -o ./tmp/api ./cmd/api" + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + silent = false + time = false + +[misc] + clean_on_exit = false + +[proxy] + app_port = 0 + enabled = false + proxy_port = 0 + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0c30068 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,33 @@ +.git +.gitignore +.github/ +.circleci/ +.gitlab-ci.yml +.idea/ +.vscode/ +.DS_Store + +# Never send local secrets or editor/dev-only files into the build context. +.env +.env.* +!.env.example +.air.toml + +# Test and coverage artifacts are not needed for production image builds. +**/*_test.go +coverage.out +coverage.html +*.cover +*.log + +# Exclude docs and directories not needed by the production Dockerfile. +docs/*.md +docs/collections/ +docs/swagger.json +docs/swagger.yaml +docs/docs_test.go +deployments/ +migrations/ +tmp/ +dist/ +bin/ diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..de77183 --- /dev/null +++ b/.env.local.example @@ -0,0 +1,248 @@ +# Copy this to .env.local and fill in your values. +# Never commit .env.local. + +# App +APP_ENV=local + +# HTTP +HTTP_PORT=8080 +HTTP_BODY_LIMIT=4194304 +HTTP_READ_TIMEOUT=10s +HTTP_WRITE_TIMEOUT=15s +HTTP_IDLE_TIMEOUT=60s +HTTP_CORS_ALLOW_ORIGINS=http://localhost:3000,http://127.0.0.1:3000,http://localhost:5173,http://127.0.0.1:5173 +HTTP_CORS_ALLOW_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD +HTTP_CORS_ALLOW_HEADERS=Origin,Content-Type,Accept,Authorization,X-Requested-With,X-Pin-Verification-Token,X-Device-Type +HTTP_CORS_EXPOSE_HEADERS=X-Request-ID +HTTP_CORS_ALLOW_CREDENTIALS=true +HTTP_CORS_MAX_AGE=300 +HTTP_REQUEST_TIMEOUT=30s +HTTP_REQUEST_TIMEOUT_OVERRIDES= +HTTP_SHUTDOWN_TIMEOUT=30s +HTTP_RATE_LIMIT_MAX=120 +HTTP_RATE_LIMIT_WINDOW=1m +HTTP_SECURITY_HSTS_MAX_AGE=0 + +# MySQL +MYSQL_DSN="root:password@tcp(localhost:3306)/fm_dev?parseTime=true" +MYSQL_MAX_OPEN_CONNS=25 +MYSQL_MAX_IDLE_CONNS=10 +MYSQL_CONN_MAX_LIFETIME=5m +MYSQL_CONN_MAX_IDLE_TIME=2m + +# Logging +LOG_LEVEL=debug +LOG_FORMAT=json + +# Audit +AUDIT_LOG_QUEUE_SIZE=1024 +AUDIT_LOG_WORKERS=2 + +# Email +EMAIL_PROVIDER=mock +EMAIL_MOCK_DIR=.local/emails + +# Circuit Breaker (default policy) +CB_DEFAULT_ENABLED=true +CB_DEFAULT_MAX_REQUESTS=1 +CB_DEFAULT_INTERVAL=1m +CB_DEFAULT_BUCKET_PERIOD=10s +CB_DEFAULT_TIMEOUT=30s +CB_DEFAULT_MIN_REQUESTS=5 +CB_DEFAULT_FAILURE_RATIO=0.6 +CB_DEFAULT_CONSECUTIVE_FAILURES=5 +CB_MICROSOFT_SSO_ENABLED=true +CB_MICROSOFT_SSO_MAX_REQUESTS=1 +CB_MICROSOFT_SSO_INTERVAL=1m +CB_MICROSOFT_SSO_BUCKET_PERIOD=10s +CB_MICROSOFT_SSO_TIMEOUT=30s +CB_MICROSOFT_SSO_MIN_REQUESTS=5 +CB_MICROSOFT_SSO_FAILURE_RATIO=0.6 +CB_MICROSOFT_SSO_CONSECUTIVE_FAILURES=5 +CB_SES_ENABLED=true +CB_SES_MAX_REQUESTS=1 +CB_SES_INTERVAL=1m +CB_SES_BUCKET_PERIOD=10s +CB_SES_TIMEOUT=30s +CB_SES_MIN_REQUESTS=5 +CB_SES_FAILURE_RATIO=0.6 +CB_SES_CONSECUTIVE_FAILURES=5 +CB_SQS_ENABLED=true +CB_SQS_MAX_REQUESTS=1 +CB_SQS_INTERVAL=1m +CB_SQS_BUCKET_PERIOD=10s +CB_SQS_TIMEOUT=30s +CB_SQS_MIN_REQUESTS=5 +CB_SQS_FAILURE_RATIO=0.6 +CB_SQS_CONSECUTIVE_FAILURES=5 + +# Auth +AUTH_PASSWORD_PEPPER=change-me-local-pepper +AUTH_IP_ENCRYPTION_KEY=change-me-local-ip-key +AUTH_DEFAULT_ROLE=user +AUTH_DISABLE_REGISTER=false +AUTH_HASH_TIME=1 +AUTH_HASH_MEMORY_KB=65536 +AUTH_HASH_THREADS=4 +AUTH_HASH_KEY_LEN=32 +AUTH_EMAIL_VERIFY_TTL=24h +AUTH_EMAIL_VERIFY_URL=http://localhost:3000/verify-email +AUTH_INVITE_TTL=24h +AUTH_SET_PASSWORD_URL=http://localhost:3000/set-password +AUTH_PASSWORD_RESET_TTL=20m +AUTH_PASSWORD_RESET_URL=http://localhost:3000/reset-password +AUTH_FORGOT_PASSWORD_EMAIL_COOLDOWN=2m +AUTH_FORGOT_PASSWORD_MIN_DURATION=150ms +AUTH_FORGOT_PASSWORD_IP_RATE_MAX=10 +AUTH_FORGOT_PASSWORD_IP_RATE_WINDOW=15m +AUTH_SECURITY_PIN_RESET_TTL=20m +AUTH_SECURITY_PIN_RESET_URL=http://localhost:3000/reset-pin +AUTH_FORGOT_SECURITY_PIN_EMAIL_COOLDOWN=2m +AUTH_FORGOT_SECURITY_PIN_MIN_DURATION=150ms +AUTH_SECURITY_PIN_MAX_ATTEMPTS=5 +AUTH_SECURITY_PIN_LOCK_DURATION=0s +AUTH_SECURITY_PIN_ACTION_TOKEN_TTL=5m + +AUTH_JWT_COOKIE_DOMAIN=.mybit.co.id + +# JWT +AUTH_JWT_ACCESS_SECRET=change-me-local-access-secret +AUTH_JWT_REFRESH_SECRET=change-me-local-refresh-secret +AUTH_JWT_ACCESS_TTL=10m +AUTH_JWT_REFRESH_TTL=1h +AUTH_JWT_REFRESH_TOTP_TTL=1h +AUTH_JWT_COOKIE_SECURE=false +AUTH_JWT_COOKIE_SAMESITE=Lax +AUTH_JWT_ACCESS_COOKIE=wucher_at +AUTH_JWT_REFRESH_COOKIE=wucher_rt + +# TOTP +AUTH_TOTP_CHALLENGE_TTL=5m +TOTP_ISSUER=Wucher +TOTP_SECRET_KEY=change-me-local-totp-secret + +# SSO +AUTH_SSO_STATE_TTL=10m +AUTH_SSO_SUCCESS_REDIRECT=http://localhost:3000/sso/callback + +# WebAuthn +AUTH_WEBAUTHN_RP_ID=localhost +AUTH_WEBAUTHN_RP_DISPLAY_NAME=Wucher +AUTH_WEBAUTHN_RP_ORIGINS=http://localhost:3000 +AUTH_WEBAUTHN_CHALLENGE_TTL=5m + +# Microsoft SSO +MS_ENTRA_TENANT_ID= +MS_ENTRA_CLIENT_ID= +MS_ENTRA_CLIENT_SECRET= +MS_ENTRA_REDIRECT_URL=http://localhost:8080/v1/auth/sso/microsoft/callback +MS_ENTRA_AUTHORITY=https://login.microsoftonline.com/ +MS_ENTRA_SCOPES=openid,profile,email,offline_access,User.Read + +# Queue +QUEUE_MESSAGE_SCHEMA_VERSION=v2 +QUEUE_ENABLE_LEGACY_PAYLOAD_FALLBACK=true +QUEUE_IDEMPOTENCY_KEY_PREFIX=queue:idempotency:email: +QUEUE_IDEMPOTENCY_TTL=24h +QUEUE_PROCESSING_TTL=15m +QUEUE_WORKER_CONCURRENCY=4 +QUEUE_WORKER_MAX_BATCH_SIZE=10 +QUEUE_WORKER_PROCESS_TIMEOUT=30s +QUEUE_WORKER_SHUTDOWN_TIMEOUT=30s +QUEUE_WORKER_BACKOFF_MIN=1s +QUEUE_WORKER_BACKOFF_MAX=5m +QUEUE_WORKER_PERMANENT_FAILURE_DELAY=30s +QUEUE_WORKER_MONITOR_ADDR=127.0.0.1:9090 +QUEUE_WORKER_HEALTH_ERROR_THRESHOLD=3 +QUEUE_WORKER_DEPTH_POLL_INTERVAL=30s +QUEUE_OUTBOX_ENABLED=true +QUEUE_OUTBOX_POLL_INTERVAL=1s +QUEUE_OUTBOX_BATCH_SIZE=10 +QUEUE_OUTBOX_DISPATCH_TIMEOUT=10s +QUEUE_OUTBOX_LOCK_TTL=1m +QUEUE_OUTBOX_MAX_ATTEMPTS=20 + +# File Queue +FILE_QUEUE_MESSAGE_SCHEMA_VERSION=v1 +FILE_QUEUE_IDEMPOTENCY_KEY_PREFIX=queue:idempotency:file: +FILE_QUEUE_IDEMPOTENCY_TTL=24h +FILE_QUEUE_PROCESSING_TTL=15m +FILE_QUEUE_WORKER_CONCURRENCY=4 +FILE_QUEUE_WORKER_MAX_BATCH_SIZE=10 +FILE_QUEUE_WORKER_PROCESS_TIMEOUT=30s +FILE_QUEUE_WORKER_SHUTDOWN_TIMEOUT=30s +FILE_QUEUE_WORKER_BACKOFF_MIN=1s +FILE_QUEUE_WORKER_BACKOFF_MAX=5m +FILE_QUEUE_WORKER_PERMANENT_FAILURE_DELAY=30s +FILE_QUEUE_WORKER_MONITOR_ADDR=127.0.0.1:9091 +FILE_QUEUE_WORKER_HEALTH_ERROR_THRESHOLD=3 +FILE_QUEUE_WORKER_DEPTH_POLL_INTERVAL=30s +FILE_QUEUE_OUTBOX_ENABLED=true +FILE_QUEUE_OUTBOX_POLL_INTERVAL=1s +FILE_QUEUE_OUTBOX_BATCH_SIZE=10 +FILE_QUEUE_OUTBOX_DISPATCH_TIMEOUT=10s +FILE_QUEUE_OUTBOX_LOCK_TTL=1m +FILE_QUEUE_OUTBOX_MAX_ATTEMPTS=20 + +# AWS +AWS_REGION=us-east-1 +AWS_ACCESS_KEY_ID=test +AWS_SECRET_ACCESS_KEY=test + +# SQS (local uses localstack or similar) +SQS_ENDPOINT=http://localhost:4566 +SQS_ALLOW_INSECURE_ENDPOINT=true +SQS_QUEUE_URL=http://localhost:4566/000000000000/fm-email-queue +SQS_QUEUE_TYPE=standard +SQS_MESSAGE_GROUP_ID=email +SQS_MAX_MESSAGES=10 +SQS_WAIT_TIME_SECONDS=20 +SQS_VISIBILITY_TIMEOUT=60s + +# SQS (file queue, local uses localstack or similar) +FILE_SQS_REGION=us-east-1 +FILE_SQS_ENDPOINT=http://localhost:4566 +FILE_SQS_ALLOW_INSECURE_ENDPOINT=true +FILE_SQS_QUEUE_URL=http://localhost:4566/000000000000/fm-file-queue +FILE_SQS_QUEUE_TYPE=standard +FILE_SQS_MESSAGE_GROUP_ID=file-processing +FILE_SQS_MAX_MESSAGES=10 +FILE_SQS_WAIT_TIME_SECONDS=20 +FILE_SQS_VISIBILITY_TIMEOUT=60s + +# SES (local uses localstack or similar) +SES_REGION=us-east-1 +SES_FROM=noreply@localhost +SES_CONFIGURATION_SET= +SES_SEND_TIMEOUT=10s +SES_ENDPOINT=http://localhost:4566 +SES_ALLOW_INSECURE_ENDPOINT=true + +# File Storage (local uses localstack or similar) +FILE_STORAGE_PROVIDER=s3 +FILE_S3_BUCKET=fm-local +FILE_S3_REGION=us-east-1 +FILE_S3_ENDPOINT=http://localhost:4566 +FILE_S3_FORCE_PATH_STYLE=true +FILE_S3_ALLOW_INSECURE_ENDPOINT=true +FILE_S3_AUTO_CREATE_BUCKET=true +FILE_OBJECT_KEY_PREFIX=fm/objects +FILE_S3_PRESIGN_PUT_TTL=15m +FILE_S3_PRESIGN_GET_TTL=15m +FILE_MANAGER_UPLOAD_MAX_FILE_SIZE_BYTES=4194304 +FILE_MANAGER_UPLOAD_ALLOWED_MIME_TYPES=image/png,image/jpeg,application/pdf,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel +FILE_MANAGER_UPLOAD_ALLOWED_EXTENSIONS=png,jpg,jpeg,pdf,xlsx,xls +FILE_MANAGER_UPLOAD_ALLOW_EMPTY_FILE=false +FILE_MANAGER_UPLOAD_MAX_FILENAME_LENGTH=255 + +FILE_MANAGER_TRASH_AUTO_PURGE_ENABLED=true +FILE_MANAGER_TRASH_RETENTION=720h +FILE_MANAGER_TRASH_AUTO_PURGE_INTERVAL=1h +FILE_MANAGER_TRASH_AUTO_PURGE_BATCH_SIZE=200 + +ENABLE_S3_TAGGING=true +ENABLE_FILE_CLEANUP_JOB=false +TEMP_FILE_TTL_DAYS=7 +ORPHAN_GRACE_PERIOD_DAYS=30 +FILE_CLEANUP_BATCH_SIZE=100 +FILE_CLEANUP_DRY_RUN=true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcae96b --- /dev/null +++ b/.gitignore @@ -0,0 +1,62 @@ +# Go +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out +*.prof + +# Build +/bin/ +/build/ +/dist/ +/tmp/ + +# Coverage +coverage.out +coverage.txt + +# Go workspace +/vendor/ + +# IDE/editor +.vscode/ +.idea/ +*.swp +*.swo +*.swn +*.tmp + +# OS +.DS_Store +Thumbs.db + +# Env files +.env +.env.* +!.env.local.example + +# Generated configs +*.generated.json + +# Logs +*.log + +# Docker +**/.docker/ + +# GoFiber (optional) +# (No special ignores required beyond Go build artifacts) + +# Claude Config +.claude/ + +# Codex Config +.codex + +# Graphify outputs +**/graphify-out/ +.graphify-venv/ +AGENTS.md diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..64786df --- /dev/null +++ b/.releaserc @@ -0,0 +1,16 @@ +{ + "branches": [ + "main", + { "name": "staging", "prerelease": "rc" } + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@saithodev/semantic-release-gitea", + { + "giteaUrl": "https://repo.mybit.center" + } + ] + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d7315cf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,109 @@ +# syntax=docker/dockerfile:1.7 + +# Override these in CI with immutable digests when you are ready to pin them fully. +ARG GO_IMAGE=golang:1.26.2-alpine3.22 +ARG RUNTIME_IMAGE=gcr.io/distroless/static-debian12:nonroot + +FROM ${GO_IMAGE} AS build-base + +WORKDIR /src + +ENV CGO_ENABLED=0 \ + GOOS=linux \ + GOFLAGS=-buildvcs=false + +COPY go.mod go.sum ./ +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + --mount=type=cache,target=/root/.cache/go-build,sharing=locked \ + go mod download && go mod verify + +FROM build-base AS healthcheck-build + +COPY cmd/healthcheck/ ./cmd/healthcheck/ +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + --mount=type=cache,target=/root/.cache/go-build,sharing=locked \ + go build \ + -trimpath \ + -ldflags="-s -w" \ + -o /out/healthcheck \ + ./cmd/healthcheck + +FROM build-base AS worker-build + +COPY cmd/worker/ ./cmd/worker/ +COPY internal/ ./internal/ +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + --mount=type=cache,target=/root/.cache/go-build,sharing=locked \ + go build \ + -trimpath \ + -ldflags="-s -w" \ + -o /out/wucher-worker \ + ./cmd/worker +RUN mkdir -p /out/.local/emails && touch /out/.local/emails/.keep + +FROM build-base AS file-worker-build + +COPY cmd/worker/ ./cmd/worker/ +COPY internal/ ./internal/ +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + --mount=type=cache,target=/root/.cache/go-build,sharing=locked \ + go build \ + -trimpath \ + -ldflags="-s -w" \ + -o /out/wucher-file-worker \ + ./cmd/worker + +FROM build-base AS api-build + +ARG VERSION=unknown +ARG COMMIT=unknown +ARG BUILD_TIME=unknown + +COPY cmd/api/ ./cmd/api/ +COPY internal/ ./internal/ +COPY docs/docs.go ./docs/docs.go +RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \ + --mount=type=cache,target=/root/.cache/go-build,sharing=locked \ + go build \ + -trimpath \ + -ldflags="-s -w -X 'wucher/internal/buildinfo.Version=${VERSION}' -X 'wucher/internal/buildinfo.Commit=${COMMIT}' -X 'wucher/internal/buildinfo.BuildTime=${BUILD_TIME}'" \ + -o /out/wucher-api \ + ./cmd/api + +FROM ${RUNTIME_IMAGE} AS runtime-base + +WORKDIR /app + +FROM runtime-base AS worker + +COPY --from=healthcheck-build --chown=nonroot:nonroot /out/healthcheck /app/healthcheck +COPY --from=worker-build --chown=nonroot:nonroot /out/wucher-worker /app/wucher-worker +COPY --from=worker-build --chown=nonroot:nonroot /out/.local/emails/ /app/.local/emails/ + +ENV WORKER_JOB=email + +USER nonroot:nonroot + +ENTRYPOINT ["/app/wucher-worker"] + +FROM runtime-base AS file-worker + +COPY --from=healthcheck-build --chown=nonroot:nonroot /out/healthcheck /app/healthcheck +COPY --from=file-worker-build --chown=nonroot:nonroot /out/wucher-file-worker /app/wucher-file-worker + +ENV WORKER_JOB=file_processing + +USER nonroot:nonroot + +ENTRYPOINT ["/app/wucher-file-worker"] + +FROM runtime-base AS api + +COPY --from=healthcheck-build --chown=nonroot:nonroot /out/healthcheck /app/healthcheck +COPY --from=api-build --chown=nonroot:nonroot /out/wucher-api /app/wucher-api + +USER nonroot:nonroot + +EXPOSE 8080 + +ENTRYPOINT ["/app/wucher-api"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2fc2a46 --- /dev/null +++ b/Makefile @@ -0,0 +1,107 @@ +GO ?= go +AIR ?= air +SWAG ?= swag +GOOSE ?= goose +GOOSE_DIR ?= migrations/goose +MIGRATE_APPLY_FROM ?= 20260414 + +.PHONY: air swag test worker file-worker migrate-status migrate-up migrate-up-to migrate-apply migrate-reapply hospital-apply + +air: + @command -v $(AIR) >/dev/null 2>&1 || { \ + echo "air not found. Install with: go install github.com/air-verse/air@latest"; \ + exit 1; \ + } + $(AIR) -c .air.toml + +swag: + @command -v $(SWAG) >/dev/null 2>&1 || { \ + echo "swag not found. Install with: go install github.com/swaggo/swag/cmd/swag@latest"; \ + exit 1; \ + } + GOCACHE=$${GOCACHE:-/tmp/go-cache} $(SWAG) init -g cmd/api/main.go -o docs --parseDependency --parseInternal + +test: + $(GO) test ./... -coverprofile=coverage.out + $(GO) tool cover -func=coverage.out + +worker: + WORKER_JOB=email $(GO) run ./cmd/worker + +file-worker: + WORKER_JOB=file_processing $(GO) run ./cmd/worker + +migrate-status: + @command -v $(GOOSE) >/dev/null 2>&1 || { \ + echo "goose not found. Install with: go install github.com/pressly/goose/v3/cmd/goose@latest"; \ + exit 1; \ + } + @MYSQL_DSN_VAL="$${MYSQL_DSN:-$$(grep -hE '^MYSQL_DSN=' .env .env.local 2>/dev/null | tail -n1 | cut -d= -f2-)}"; \ + MYSQL_DSN_VAL="$${MYSQL_DSN_VAL#\"}"; MYSQL_DSN_VAL="$${MYSQL_DSN_VAL%\"}"; \ + test -n "$$MYSQL_DSN_VAL" || { echo "MYSQL_DSN is required (set env or define in .env/.env.local)"; exit 1; }; \ + $(GOOSE) -dir $(GOOSE_DIR) mysql "$$MYSQL_DSN_VAL" status + +migrate-up: + @command -v $(GOOSE) >/dev/null 2>&1 || { \ + echo "goose not found. Install with: go install github.com/pressly/goose/v3/cmd/goose@latest"; \ + exit 1; \ + } + @MYSQL_DSN_VAL="$${MYSQL_DSN:-$$(grep -hE '^MYSQL_DSN=' .env .env.local 2>/dev/null | tail -n1 | cut -d= -f2-)}"; \ + MYSQL_DSN_VAL="$${MYSQL_DSN_VAL#\"}"; MYSQL_DSN_VAL="$${MYSQL_DSN_VAL%\"}"; \ + test -n "$$MYSQL_DSN_VAL" || { echo "MYSQL_DSN is required (set env or define in .env/.env.local)"; exit 1; }; \ + $(GOOSE) -dir $(GOOSE_DIR) mysql "$$MYSQL_DSN_VAL" up + +migrate-up-to: + @command -v $(GOOSE) >/dev/null 2>&1 || { \ + echo "goose not found. Install with: go install github.com/pressly/goose/v3/cmd/goose@latest"; \ + exit 1; \ + } + @MYSQL_DSN_VAL="$${MYSQL_DSN:-$$(grep -hE '^MYSQL_DSN=' .env .env.local 2>/dev/null | tail -n1 | cut -d= -f2-)}"; \ + MYSQL_DSN_VAL="$${MYSQL_DSN_VAL#\"}"; MYSQL_DSN_VAL="$${MYSQL_DSN_VAL%\"}"; \ + test -n "$$MYSQL_DSN_VAL" || { echo "MYSQL_DSN is required (set env or define in .env/.env.local)"; exit 1; }; \ + test -n "$(v)" || { echo "usage: make migrate-up-to v=20260414"; exit 1; }; \ + $(GOOSE) -dir $(GOOSE_DIR) mysql "$$MYSQL_DSN_VAL" up-to $(v) + +migrate-apply: + @command -v $(GOOSE) >/dev/null 2>&1 || { \ + echo "goose not found. Install with: go install github.com/pressly/goose/v3/cmd/goose@latest"; \ + exit 1; \ + } + @MYSQL_DSN_VAL="$${MYSQL_DSN:-$$(grep -hE '^MYSQL_DSN=' .env .env.local 2>/dev/null | tail -n1 | cut -d= -f2-)}"; \ + MYSQL_DSN_VAL="$${MYSQL_DSN_VAL#\"}"; MYSQL_DSN_VAL="$${MYSQL_DSN_VAL%\"}"; \ + test -n "$$MYSQL_DSN_VAL" || { echo "MYSQL_DSN is required (set env or define in .env/.env.local)"; exit 1; }; \ + MODULES="$(modules)"; \ + test -n "$$MODULES" || { \ + echo "usage: make migrate-apply modules=opc,kassa"; \ + echo "hint: use 'make migrate-reapply [from=YYYYMMDD]' for full down-to/up behavior"; \ + exit 1; \ + }; \ + TMP_DIR="$$(mktemp -d)"; \ + echo "Applying selected import apply migrations for modules: $$MODULES"; \ + for m in $$(echo "$$MODULES" | tr ',' ' '); do \ + FOUND="$$(ls -1 $(GOOSE_DIR)/*_$${m}_import_apply.sql 2>/dev/null || true)"; \ + if [ -z "$$FOUND" ]; then \ + echo "No apply migration found for module '$$m' in $(GOOSE_DIR)"; \ + rm -rf "$$TMP_DIR"; \ + exit 1; \ + fi; \ + for f in $$FOUND; do cp "$$f" "$$TMP_DIR"/; done; \ + done; \ + $(GOOSE) -dir "$$TMP_DIR" -no-versioning mysql "$$MYSQL_DSN_VAL" up; \ + rm -rf "$$TMP_DIR" + +migrate-reapply: + @command -v $(GOOSE) >/dev/null 2>&1 || { \ + echo "goose not found. Install with: go install github.com/pressly/goose/v3/cmd/goose@latest"; \ + exit 1; \ + } + @MYSQL_DSN_VAL="$${MYSQL_DSN:-$$(grep -hE '^MYSQL_DSN=' .env .env.local 2>/dev/null | tail -n1 | cut -d= -f2-)}"; \ + MYSQL_DSN_VAL="$${MYSQL_DSN_VAL#\"}"; MYSQL_DSN_VAL="$${MYSQL_DSN_VAL%\"}"; \ + test -n "$$MYSQL_DSN_VAL" || { echo "MYSQL_DSN is required (set env or define in .env/.env.local)"; exit 1; }; \ + FROM_VER="$(from)"; \ + if [ -z "$$FROM_VER" ]; then FROM_VER="$(MIGRATE_APPLY_FROM)"; fi; \ + echo "Re-applying migrations from version $$FROM_VER to latest"; \ + $(GOOSE) -dir $(GOOSE_DIR) mysql "$$MYSQL_DSN_VAL" down-to "$$FROM_VER"; \ + $(GOOSE) -dir $(GOOSE_DIR) mysql "$$MYSQL_DSN_VAL" up + +hospital-apply: migrate-apply diff --git a/README.md b/README.md new file mode 100644 index 0000000..52a17d7 --- /dev/null +++ b/README.md @@ -0,0 +1,435 @@ +# Wucher Backend + +Wucher is a Go backend built with GoFiber. The runtime uses MySQL for persistence and transient auth state, Amazon SQS for asynchronous email jobs, and Amazon SES v2 for email delivery. + +## Prerequisites + +- Go 1.25+ +- MySQL +- LocalStack or AWS SQS/SES access for queue testing + +## Project Structure + +- `cmd/api/` API bootstrap +- `cmd/worker/` SQS worker bootstrap +- `internal/app/` application wiring +- `internal/resilience/` circuit breaker executor and dependency error classifiers +- `internal/service/` business logic +- `internal/transport/http/` GoFiber handlers and middleware +- `internal/repository/mysql/` MySQL repositories and transient stores +- `internal/repository/sqs/` SQS publisher/consumer adapter +- `internal/queue/` queue contracts, serializer, retry policy, and worker loop + +## Runtime Architecture + +- HTTP handlers stay thin and delegate to services. +- Auth and other business writes persist to MySQL first. +- API/service code never sends email synchronously. +- Email jobs are written to `email_outbox_messages` when outbox is enabled, or published directly to SQS when outbox is disabled. +- Worker publishes outbox rows to the SQS send queue, consumes SQS messages with long polling, and sends email through Amazon SES v2. +- SES bounce, complaint, and delivery tracking should use a separate flow: + - SES Configuration Set + - SNS topic subscription + - dedicated SQS event queue +- Circuit breakers are applied only at outbound dependency boundaries: + - Microsoft Entra token exchange + - SES delivery + - SQS publish/consume/ack calls +- Transient auth state previously kept in Redis is now stored in MySQL tables: + - `auth_runtime_tokens` + - `queue_idempotency_records` + +`gorm` auto-migration creates these tables on startup. + +## Getting Started + +Install dependencies: + +```bash +go mod tidy +``` + +Create local config from the example: + +```bash +cp .env.example .env +``` + +Run the API: + +```bash +go run ./cmd/api +``` + +Run the worker: + +```bash +go run ./cmd/worker +``` + +Worker monitor endpoints: + +```bash +curl http://127.0.0.1:9090/health +curl http://127.0.0.1:9090/metrics +curl http://127.0.0.1:9091/health +curl http://127.0.0.1:9091/metrics +``` + +- `/metrics` exposes Prometheus format for API and worker processes. +- `/health` remains JSON health/worker snapshot. + +## Environment Variables + +Example values are in `.env` for local development. + +Important groups: + +- Email delivery + - `EMAIL_PROVIDER` + - `EMAIL_MOCK_DIR` +- MySQL + - `MYSQL_DSN` + - `MYSQL_MAX_OPEN_CONNS` + - `MYSQL_MAX_IDLE_CONNS` + - `MYSQL_CONN_MAX_LIFETIME` + - `MYSQL_CONN_MAX_IDLE_TIME` +- Auth + - `AUTH_EMAIL_VERIFY_URL` + - `AUTH_PASSWORD_RESET_URL` + - `AUTH_SECURITY_PIN_RESET_URL` + - `AUTH_TOTP_CHALLENGE_TTL` + - `AUTH_SSO_STATE_TTL` + - `AUTH_JWT_ACCESS_SECRET` + - `AUTH_JWT_REFRESH_SECRET` + - `AUTH_WEBAUTHN_*` +- Queue / worker + - `QUEUE_MESSAGE_SCHEMA_VERSION` + - `QUEUE_ENABLE_LEGACY_PAYLOAD_FALLBACK` + - `QUEUE_IDEMPOTENCY_KEY_PREFIX` + - `QUEUE_IDEMPOTENCY_TTL` + - `QUEUE_PROCESSING_TTL` + - `QUEUE_WORKER_CONCURRENCY` + - `QUEUE_WORKER_MAX_BATCH_SIZE` + - `QUEUE_WORKER_PROCESS_TIMEOUT` + - `QUEUE_WORKER_BACKOFF_MIN` + - `QUEUE_WORKER_BACKOFF_MAX` + - `QUEUE_WORKER_PERMANENT_FAILURE_DELAY` + - `QUEUE_OUTBOX_ENABLED` + - `QUEUE_OUTBOX_POLL_INTERVAL` + - `QUEUE_OUTBOX_BATCH_SIZE` + - `QUEUE_OUTBOX_DISPATCH_TIMEOUT` + - `QUEUE_OUTBOX_LOCK_TTL` + - `QUEUE_OUTBOX_MAX_ATTEMPTS` +- File queue / file worker + - `FILE_QUEUE_MESSAGE_SCHEMA_VERSION` + - `FILE_QUEUE_IDEMPOTENCY_KEY_PREFIX` + - `FILE_QUEUE_IDEMPOTENCY_TTL` + - `FILE_QUEUE_PROCESSING_TTL` + - `FILE_QUEUE_WORKER_CONCURRENCY` + - `FILE_QUEUE_WORKER_MAX_BATCH_SIZE` + - `FILE_QUEUE_WORKER_PROCESS_TIMEOUT` + - `FILE_QUEUE_WORKER_SHUTDOWN_TIMEOUT` + - `FILE_QUEUE_WORKER_BACKOFF_MIN` + - `FILE_QUEUE_WORKER_BACKOFF_MAX` + - `FILE_QUEUE_WORKER_PERMANENT_FAILURE_DELAY` + - `FILE_QUEUE_WORKER_MONITOR_ADDR` + - `FILE_QUEUE_WORKER_HEALTH_ERROR_THRESHOLD` + - `FILE_QUEUE_WORKER_DEPTH_POLL_INTERVAL` + - `FILE_QUEUE_OUTBOX_ENABLED` + - `FILE_QUEUE_OUTBOX_POLL_INTERVAL` + - `FILE_QUEUE_OUTBOX_BATCH_SIZE` + - `FILE_QUEUE_OUTBOX_DISPATCH_TIMEOUT` + - `FILE_QUEUE_OUTBOX_LOCK_TTL` + - `FILE_QUEUE_OUTBOX_MAX_ATTEMPTS` +- Circuit breakers + - `CB_DEFAULT_ENABLED` + - `CB_DEFAULT_MAX_REQUESTS` + - `CB_DEFAULT_INTERVAL` + - `CB_DEFAULT_BUCKET_PERIOD` + - `CB_DEFAULT_TIMEOUT` + - `CB_DEFAULT_MIN_REQUESTS` + - `CB_DEFAULT_FAILURE_RATIO` + - `CB_DEFAULT_CONSECUTIVE_FAILURES` + - `CB_MICROSOFT_SSO_ENABLED` + - `CB_SES_ENABLED` + - `CB_SQS_ENABLED` +- AWS / SES + - `AWS_REGION` + - `SES_REGION` + - `SES_FROM` + - `SES_CONFIGURATION_SET` + - `SES_SEND_TIMEOUT` + - `SES_ENDPOINT` + - `SES_ALLOW_INSECURE_ENDPOINT` +- AWS / SQS + - Email queue: + - `AWS_REGION` + - `AWS_ACCESS_KEY_ID` + - `AWS_SECRET_ACCESS_KEY` + - `SQS_ENDPOINT` + - `SQS_ALLOW_INSECURE_ENDPOINT` + - `SQS_QUEUE_URL` + - `SQS_QUEUE_TYPE` + - `SQS_MESSAGE_GROUP_ID` + - `SQS_MAX_MESSAGES` + - `SQS_WAIT_TIME_SECONDS` + - `SQS_VISIBILITY_TIMEOUT` + - File queue: + - `FILE_SQS_REGION` + - `FILE_SQS_ENDPOINT` + - `FILE_SQS_QUEUE_URL` + - `FILE_SQS_QUEUE_TYPE` + - `FILE_SQS_MESSAGE_GROUP_ID` + - `FILE_SQS_MAX_MESSAGES` + - `FILE_SQS_WAIT_TIME_SECONDS` + - `FILE_SQS_VISIBILITY_TIMEOUT` + - `FILE_SQS_ALLOW_INSECURE_ENDPOINT` +- File storage (S3) + - `FILE_STORAGE_PROVIDER` + - `FILE_S3_BUCKET` + - `FILE_S3_REGION` + - `FILE_S3_ENDPOINT` + - `FILE_S3_FORCE_PATH_STYLE` + - `FILE_S3_ALLOW_INSECURE_ENDPOINT` + - `FILE_S3_AUTO_CREATE_BUCKET` + - `FILE_OBJECT_KEY_PREFIX` + - `FILE_S3_PRESIGN_PUT_TTL` + - `FILE_S3_PRESIGN_GET_TTL` +- WOPI / Collabora + - `WOPI_TOKEN_SECRET` + - `WOPI_PROOF_SECRET` + - `WOPI_DISCOVERY_URL` + - `WOPI_PUBLIC_BASE_URL` + - `WOPI_EDIT_ACTION_URL` + - `WOPI_TOKEN_TTL` + - `WOPI_MAX_BODY_BYTES` + - `WOPI_REQUIRE_PROOF` +- Worker mode selector + - `WORKER_JOB=email` + - `WORKER_JOB=file_processing` + +## WOPI / Collabora Hardening + +- Keep WOPI endpoints behind the dedicated `/wopi` router and do not expose them through the session-authenticated app middleware chain. +- Use HTTPS end to end in production. +- Restrict network access to the Collabora host or private network allowlist. +- Set Collabora `aliasgroup`/host configuration so it can only call back to this backend. +- Keep `WOPI_TOKEN_SECRET` and `WOPI_PROOF_SECRET` in env or a secrets manager and rotate them periodically. +- Scope IAM to the minimum S3 bucket/prefix needed by file storage. +- Keep direct S3 URLs disabled for editor access; the backend should proxy file bytes. +- Ensure backend logs capture open/save/exit-save events for auditability. +- Verify `/hosting/discovery` reachability and S3 connectivity in health checks before enabling Collabora traffic. + +Per-dependency overrides are supported with the same shape as the default block: + +- `CB_MICROSOFT_SSO_*` +- `CB_SES_*` +- `CB_SQS_*` + +Example: `CB_SES_TIMEOUT=45s` or `CB_SQS_CONSECUTIVE_FAILURES=3`. + +## Circuit Breakers + +- Breakers are scoped per outbound dependency, not per HTTP endpoint and not global middleware. +- MySQL repositories and internal business logic are intentionally not wrapped. +- Only dependency-health failures count toward opening the breaker. + - Microsoft SSO: network errors, timeouts, HTTP `429`, and `5xx` + - SES: network errors, throttling, and AWS server faults + - SQS: network errors, throttling, and AWS server faults +- Client/config/input errors are excluded so they do not open the breaker. +- State transitions are logged through the standard application logger. + +## Queue Behavior + +- Long polling is enabled through `SQS_WAIT_TIME_SECONDS`. +- Messages are deleted only after successful processing or explicit ack of duplicates. +- Transient failures use `ChangeMessageVisibility` with backoff. +- Permanent failures are left unacked so SQS redrive policy can move them to the DLQ. +- The default producer schema is `v2`; legacy payload fallback remains available for older messages. +- Configure DLQ on the queue itself in AWS/LocalStack. The application no longer publishes directly to a DLQ URL. + +## Prometheus Metrics + +Scrape endpoints: + +- API: `http://:8080/metrics` +- Email worker monitor: `http://:9090/metrics` +- File worker monitor: `http://:9091/metrics` + +Example Prometheus scrape config: + +```yaml +scrape_configs: + - job_name: "wucher-api" + static_configs: + - targets: ["127.0.0.1:8080"] + - job_name: "wucher-email-worker" + static_configs: + - targets: ["127.0.0.1:9090"] + - job_name: "wucher-file-worker" + static_configs: + - targets: ["127.0.0.1:9091"] +``` + +Metrics and labels: + +- `app_aws_requests_total{component,service,operation,result}` +- `app_aws_request_duration_seconds{component,service,operation}` +- `app_aws_errors_total{component,service,operation,error_class}` where `error_class` is `4xx`, `5xx`, or `other` +- `app_aws_transferred_bytes_total{component,service,operation,direction}` +- `app_queue_messages_received_total{component,queue_name}` +- `app_queue_messages_deleted_total{component,queue_name}` +- `app_queue_visibility_changes_total{component,queue_name,result}` +- `app_queue_send_total{component,queue_name,result}` +- `app_queue_retries_total{component,queue_name,result}` +- `app_queue_message_bytes_total{component,queue_name,direction}` +- `app_queue_depth{component,queue_name}` +- `app_worker_jobs_processed_total{component,job_type,result}` +- `app_worker_job_duration_seconds{component,job_type}` +- `app_email_send_total{component,provider,result}` +- `app_email_send_duration_seconds{component,provider}` +- `app_file_upload_total{component,result}` +- `app_file_delete_total{component,result}` +- `app_file_head_total{component,result}` +- `app_file_presign_download_total{component,result}` + +PromQL examples: + +- Error rate per AWS operation: + +```promql +sum by (component, service, operation) (rate(app_aws_requests_total{result="error"}[5m])) +/ +clamp_min(sum by (component, service, operation) (rate(app_aws_requests_total[5m])), 1e-9) +``` + +- P95 latency per AWS operation: + +```promql +histogram_quantile( + 0.95, + sum by (le, component, service, operation) ( + rate(app_aws_request_duration_seconds_bucket[5m]) + ) +) +``` + +- Queue depth: + +```promql +max by (component, queue_name) (app_queue_depth) +``` + +- Worker success/failure count: + +```promql +sum by (component, job_type, result) (increase(app_worker_jobs_processed_total[15m])) +``` + +- SES send failure rate: + +```promql +sum by (component) (rate(app_email_send_total{provider="ses", result="error"}[5m])) +/ +clamp_min(sum by (component) (rate(app_email_send_total{provider="ses"}[5m])), 1e-9) +``` + +- S3 4xx/5xx error rate: + +```promql +sum by (operation, error_class) (rate(app_aws_errors_total{service="s3"}[5m])) +``` + +- S3 GetObject transferred bytes (estimated from presign download flow): + +```promql +sum by (component) (rate(app_aws_transferred_bytes_total{service="s3",operation="getobject",direction="download"}[5m])) +``` + +- SQS message bytes in/out: + +```promql +sum by (component, queue_name, direction) (rate(app_queue_message_bytes_total[5m])) +``` + +- SQS retry/requeue count: + +```promql +sum by (component, queue_name, result) (increase(app_queue_retries_total[15m])) +``` + +## File Manager + +- Queue separation: + - Email queue: `app-queue` (+ `app-queue-dlq`) + - File queue: `app-file-queue` (+ `app-file-queue-dlq`) +- `FILE_SQS_QUEUE_URL` must be different from `SQS_QUEUE_URL`. +- API startup validates queue separation and refuses startup on misconfiguration. +- Upload writes file metadata + file outbox durably; file queue publish is handled asynchronously by the file outbox dispatcher. + +Local run checklist: + +```bash +# 1) Start localstack (queue + bucket bootstrap script will run) +cd ../localstack && docker compose up -d + +WORKER_JOB=file_processing go run ./cmd/worker +``` + +## SES Delivery Notes + +- `EMAIL_PROVIDER=mock` is the recommended local/dev default when you do not have real AWS SES access. +- Mock delivery writes one JSON file per delivered email into `EMAIL_MOCK_DIR`. +- `SES_FROM` must be a verified identity or domain in Amazon SES. +- `SES_ENDPOINT` should be blank in production and only overridden for local/dev stacks such as LocalStack. +- Do not point SES events to the main send queue. Use `SES_CONFIGURATION_SET` with SNS -> separate SQS event queue. +- The codebase includes an SES event parser for SNS-wrapped notifications, but an event consumer is not started by default. + +## Migration Notes + +Detailed migration notes are in [docs/queue-sqs-migration.md](docs/queue-sqs-migration.md). +Data migration tracking wiki is in [docs/data-migration-wiki.md](docs/data-migration-wiki.md). + +Main breaking changes: + +- Redis runtime support has been removed from the codebase. +- Redis env vars are obsolete and should be removed from deployment config. +- SMTP runtime env/config has been removed in favor of SES. +- Worker now relies on MySQL for queue idempotency and auth/runtime state persistence. +- SQS must have a redrive policy and DLQ configured outside the application. + +## CI / Build Selection + +The pipeline always builds and pushes the `api` image on every push to `development` or `staging`. + +To also build and push the worker or file-worker image, include the corresponding tag anywhere in the commit message: + +| Tag | Image built | Docker target | +|---|---|---| +| `#PUSH-WORKER` | `mybitlab/wfm:-worker` | `worker` | +| `#PUSH-FILE-WORKER` | `mybitlab/wfm:-file-worker` | `file-worker` | + +Both tags can appear in the same commit message to build all three in one pipeline run: + +``` +ci: deploy all services #PUSH-WORKER #PUSH-FILE-WORKER +``` + +Omitting a tag skips that image entirely — it will not be built or pushed. + +## Testing + +Run the full test suite: + +```bash +go test ./... +``` + +Generate coverage: + +```bash +go test ./... -coverprofile=coverage.out +go tool cover -func=coverage.out +go tool cover -html=coverage.out -o coverage.html +``` diff --git a/api b/api new file mode 100644 index 0000000..4c8daa5 Binary files /dev/null and b/api differ diff --git a/cmd/api/main.go b/cmd/api/main.go new file mode 100644 index 0000000..57e57f1 --- /dev/null +++ b/cmd/api/main.go @@ -0,0 +1,392 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + "log/slog" + "os" + "os/signal" + "syscall" + "time" + + "wucher/internal/app/api" + "wucher/internal/config" + "wucher/internal/service" + + _ "wucher/docs" +) + +var ( + apiRunFn = run + apiFatalFn = log.Fatal +) + +func configFilePath() string { + if v := os.Getenv("CONFIG_FILE"); v != "" { + return v + } + basePath := os.Getenv("CONFIG_BASE_PATH") + if basePath == "" { + basePath = "/tmp" + } + filename := os.Getenv("CONFIG_FILENAME") + if filename == "" { + filename = "config.generated.json" + } + return basePath + "/" + filename +} + +type apiServer interface { + Start(addr string) error + Shutdown(ctx context.Context) error +} + +// @title Wucher API +// @version 1.0 +// @description Wucher backend API (JSON:API). +// @description +// @description Auth Flow (Email/Password): +// @description 1) POST /api/v1/auth/register +// @description 2) Check email, open verify link (GET /api/v1/auth/verify-email) +// @description 3) POST /api/v1/auth/login +// @description - If TOTP enabled: response 202 with challenge_token +// @description 4) POST /api/v1/auth/totp/verify (challenge_token + code) -> cookies +// @description +// @description Auth Flow (TOTP setup): +// @description 1) POST /api/v1/auth/totp/setup -> secret + otpauth_url (enabled=false) +// @description 2) POST /api/v1/auth/totp/confirm -> enabled=true +// @description +// @description Auth Flow (Microsoft Entra SSO): +// @description 1) GET /api/v1/auth/microsoft/login -> redirect to Microsoft +// @description 2) Microsoft redirects to /api/v1/auth/microsoft/callback +// @description 3) Backend checks existing SSO mapping (no auto-register) +// @description 4) If linked user exists -> set cookies + optional redirect to AUTH_SSO_SUCCESS_REDIRECT +// @description 5) If mapping not linked -> login rejected (401) +// @description 6) Existing authenticated user can link/unlink via /api/v1/auth/sso/link and /api/v1/auth/sso/unlink +// @tag.name Complaints - Sign Off +// @tag.description Corrective-action sign-off for a helicopter. After a complaint's action_taken is recorded, it is signed off here (POST /action-signoffs/sign with complaint_id); a fleet-wide sign-off (no complaint_id) is also supported. A signed sign-off is the prerequisite for creating an EASA maintenance release. +// @BasePath / +func main() { + if err := apiRunFn(); err != nil { + apiFatalFn(err) + } +} + +func run() error { + appCfg, err := config.LoadFromFile(configFilePath()) + if err != nil { + return fmt.Errorf("load config: %w", err) + } + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + return runWithContext(ctx, appCfg) +} + +func runWithContext(ctx context.Context, appCfg config.AppConfig) error { + if ctx == nil { + ctx = context.Background() + } + + db, err := initializeDatabase(appCfg.MySQL) + if err != nil { + return err + } + if sqlDB, sqlErr := db.DB(); sqlErr == nil { + defer sqlDB.Close() + } + + if err := bootstrapData(db); err != nil { + return err + } + repos, err := initializeRepositories(db) + if err != nil { + return err + } + infra, err := initializeInfrastructure(ctx, appCfg, db) + if err != nil { + return err + } + services, err := initializeServices(appCfg, repos, infra) + if err != nil { + return err + } + if err := services.MasterSettings.SeedMicrosoftEntraConfigIfMissing(ctx, appCfg.MicrosoftSSO); err != nil { + return fmt.Errorf("seed microsoft entra master settings: %w", err) + } + handlersSet, err := initializeHandlers(services, infra) + if err != nil { + return err + } + if err := initializeRealtimeComponents(ctx, repos, services, infra); err != nil { + return err + } + + app, addr, err := buildHTTPServer(appCfg, services, handlersSet, infra) + if err != nil { + return err + } + srv := api.NewServer(app, api.WithShutdownTimeout(appCfg.HTTP.ShutdownTimeout)) + + infra.Logger.Info("api listening", + "addr", addr, + "queue_backend", "sqs", + "queue_outbox_enabled", appCfg.Queue.OutboxEnabled, + ) + if err := serveAPI(ctx, srv, addr); err != nil { + return err + } + infra.Logger.Info("api stopped") + return nil +} + +func serveAPI(ctx context.Context, srv apiServer, addr string) error { + if srv == nil { + return errors.New("api server is required") + } + if ctx == nil { + ctx = context.Background() + } + + serverErrCh := make(chan error, 1) + go func() { + serverErrCh <- srv.Start(addr) + }() + + select { + case err := <-serverErrCh: + return err + case <-ctx.Done(): + } + + shutdownErr := srv.Shutdown(context.Background()) + serverErr := <-serverErrCh + if shutdownErr != nil { + return shutdownErr + } + return serverErr +} + +func startFileManagerAutoPurgeLoop( + ctx context.Context, + fileManagerSvc *service.FileManagerService, + trashCfg config.FileManagerTrashPolicyConfig, + logger *slog.Logger, +) { + if fileManagerSvc == nil || !trashCfg.AutoPurgeEnabled { + return + } + interval := trashCfg.AutoPurgeInterval + if interval <= 0 { + interval = time.Hour + } + batchSize := trashCfg.AutoPurgeBatchSize + if batchSize <= 0 { + batchSize = 200 + } + + runOnce := func() { + purgedFiles, purgedFolders, err := fileManagerSvc.PurgeExpiredTrash(ctx, time.Now().UTC(), batchSize) + if err != nil { + if logger != nil { + logger.Error("file manager auto-purge run failed", "error", err) + } + return + } + if logger != nil && (purgedFiles > 0 || purgedFolders > 0) { + logger.Info( + "file manager auto-purge completed", + "purged_files", purgedFiles, + "purged_folders", purgedFolders, + "batch_size", batchSize, + ) + } + } + + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + runOnce() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + runOnce() + } + } + }() +} + +func startFleetStatusAOGRecomputeLoop( + ctx context.Context, + fleetStatusSvc *service.FleetStatusService, + logger *slog.Logger, +) { + if fleetStatusSvc == nil { + return + } + // TESTING ONLY: sweep tiap menit supaya kolom AOG + audit nyusul cepat dengan + // MEL grace yang sementara dihitung dalam menit. + // TODO: kembalikan ke time.Hour sebelum production. + interval := time.Minute + runOnce := func() { + n, err := fleetStatusSvc.RecalculateAllAOG(ctx) + if err != nil { + if logger != nil { + logger.Error("fleet status AOG recompute failed", "error", err) + } + return + } + if logger != nil { + logger.Info("fleet status AOG recompute completed", "helicopters", n) + } + } + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + runOnce() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + runOnce() + } + } + }() +} + +func startFileManagerLifecycleCleanupLoop( + ctx context.Context, + fileManagerSvc *service.FileManagerService, + lifecycleCfg config.FileManagerLifecycleConfig, + logger *slog.Logger, +) { + if fileManagerSvc == nil || !lifecycleCfg.EnableCleanupJob { + return + } + interval := time.Hour + batchSize := lifecycleCfg.CleanupBatchSize + if batchSize <= 0 { + batchSize = 100 + } + grace := time.Duration(lifecycleCfg.OrphanGraceDays) * 24 * time.Hour + tempGrace := time.Duration(lifecycleCfg.TempUploadGraceHours) * time.Hour + dryRun := lifecycleCfg.CleanupDryRun + + runOnce := func() { + stats, err := fileManagerSvc.CleanupOrphanPendingFiles(ctx, time.Now().UTC(), grace, batchSize, dryRun) + if err != nil { + if logger != nil { + logger.Error("file manager lifecycle cleanup failed", "error", err) + } + return + } + if logger != nil { + logger.Info("file manager lifecycle cleanup run completed", + "scanned", stats.Scanned, + "deleted", stats.Deleted, + "skipped", stats.Skipped, + "failed", stats.Failed, + "dry_run", stats.DryRun, + "batch_size", batchSize, + ) + } + + tempStats, err := fileManagerSvc.CleanupStaleTempUploads(ctx, time.Now().UTC(), tempGrace, batchSize, dryRun) + if err != nil { + if logger != nil { + logger.Error("file manager temp upload cleanup failed", "error", err) + } + return + } + if logger != nil { + logger.Info("file manager temp upload cleanup run completed", + "scanned", tempStats.Scanned, + "deleted", tempStats.Deleted, + "skipped", tempStats.Skipped, + "failed", tempStats.Failed, + "dry_run", tempStats.DryRun, + "batch_size", batchSize, + ) + } + + draftStats, err := fileManagerSvc.CleanupStaleDraftFiles(ctx, time.Now().UTC(), tempGrace, batchSize, dryRun) + if err != nil { + if logger != nil { + logger.Error("file manager draft cleanup failed", "error", err) + } + return + } + if logger != nil { + logger.Info("file manager draft cleanup run completed", + "scanned", draftStats.Scanned, + "deleted", draftStats.Deleted, + "skipped", draftStats.Skipped, + "failed", draftStats.Failed, + "dry_run", draftStats.DryRun, + "batch_size", batchSize, + ) + } + } + + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + runOnce() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + runOnce() + } + } + }() +} + +func startHelicopterFileAttachmentReconcileLoop( + ctx context.Context, + helicopterFileSvc *service.HelicopterFileService, + logger *slog.Logger, +) { + if helicopterFileSvc == nil { + return + } + interval := 2 * time.Second + batchSize := 50 + + runOnce := func() int { + finalized, err := helicopterFileSvc.FinalizePendingAttachments(ctx, batchSize) + if err != nil { + if logger != nil { + logger.Error("helicopter file attachment reconcile failed", "error", err) + } + return 0 + } + if logger != nil && finalized > 0 { + logger.Info("helicopter file attachment reconcile completed", "finalized", finalized, "batch_size", batchSize) + } + return finalized + } + + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + if ctx.Err() != nil { + return + } + if finalized := runOnce(); finalized > 0 { + continue + } + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + } + }() +} diff --git a/cmd/api/main_test.go b/cmd/api/main_test.go new file mode 100644 index 0000000..dcf05c5 --- /dev/null +++ b/cmd/api/main_test.go @@ -0,0 +1,153 @@ +package main + +import ( + "context" + "errors" + "sync" + "testing" +) + +func TestMain_RunSuccess(t *testing.T) { + origRun := apiRunFn + origFatal := apiFatalFn + t.Cleanup(func() { + apiRunFn = origRun + apiFatalFn = origFatal + }) + + runCalled := false + fatalCalled := false + apiRunFn = func() error { + runCalled = true + return nil + } + apiFatalFn = func(v ...any) { + fatalCalled = true + } + + main() + + if !runCalled { + t.Fatalf("expected main to call run function") + } + if fatalCalled { + t.Fatalf("did not expect fatal function on successful run") + } +} + +func TestMain_RunErrorCallsFatal(t *testing.T) { + origRun := apiRunFn + origFatal := apiFatalFn + t.Cleanup(func() { + apiRunFn = origRun + apiFatalFn = origFatal + }) + + errBoom := errors.New("boom") + fatalCalled := false + var fatalArgs []any + + apiRunFn = func() error { + return errBoom + } + apiFatalFn = func(v ...any) { + fatalCalled = true + fatalArgs = v + } + + main() + + if !fatalCalled { + t.Fatalf("expected fatal function to be called on run error") + } + if len(fatalArgs) != 1 || fatalArgs[0] != errBoom { + t.Fatalf("unexpected fatal args: %#v", fatalArgs) + } +} + +func TestServeAPI_StartError(t *testing.T) { + errBoom := errors.New("boom") + srv := &stubAPIServer{startErr: errBoom} + + err := serveAPI(context.Background(), srv, ":8080") + if !errors.Is(err, errBoom) { + t.Fatalf("expected %v, got %v", errBoom, err) + } + if srv.shutdownCalled { + t.Fatalf("did not expect shutdown to be called on start error") + } +} + +func TestServeAPI_ShutsDownOnContextCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + srv := &stubAPIServer{ + started: make(chan struct{}), + releaseStart: make(chan struct{}), + } + + errCh := make(chan error, 1) + go func() { + errCh <- serveAPI(ctx, srv, ":8080") + }() + + <-srv.started + cancel() + + if err := <-errCh; err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if !srv.shutdownCalled { + t.Fatalf("expected shutdown to be called") + } +} + +func TestServeAPI_ReturnsShutdownError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + errBoom := errors.New("shutdown failed") + srv := &stubAPIServer{ + started: make(chan struct{}), + releaseStart: make(chan struct{}), + shutdownErr: errBoom, + } + + errCh := make(chan error, 1) + go func() { + errCh <- serveAPI(ctx, srv, ":8080") + }() + + <-srv.started + cancel() + + if err := <-errCh; !errors.Is(err, errBoom) { + t.Fatalf("expected %v, got %v", errBoom, err) + } +} + +type stubAPIServer struct { + startErr error + shutdownErr error + started chan struct{} + releaseStart chan struct{} + shutdownCalled bool + releaseOnce sync.Once +} + +func (s *stubAPIServer) Start(string) error { + if s.started != nil { + close(s.started) + } + if s.releaseStart != nil { + <-s.releaseStart + } + return s.startErr +} + +func (s *stubAPIServer) Shutdown(context.Context) error { + s.shutdownCalled = true + if s.releaseStart != nil { + s.releaseOnce.Do(func() { + close(s.releaseStart) + }) + } + return s.shutdownErr +} diff --git a/cmd/api/startup.go b/cmd/api/startup.go new file mode 100644 index 0000000..89fc419 --- /dev/null +++ b/cmd/api/startup.go @@ -0,0 +1,834 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log/slog" + "os" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" + + "wucher/internal/app/api" + queueapp "wucher/internal/app/queueing" + samlauth "wucher/internal/auth" + "wucher/internal/config" + filemanager "wucher/internal/domain/file_manager" + filemanagerrealtime "wucher/internal/realtime/filemanager" + helicopterfilerealtime "wucher/internal/realtime/helicopterfile" + "wucher/internal/repository/mysql" + s3repo "wucher/internal/repository/s3" + "wucher/internal/resilience" + "wucher/internal/service" + pkglogger "wucher/internal/shared/pkg/logger" + tzpkg "wucher/internal/shared/pkg/timezone" + userctx "wucher/internal/shared/pkg/userctx" + "wucher/internal/transport/http/handlers" +) + +type Repositories struct { + User *mysql.UserRepository + Contact *mysql.ContactRepository + Role *mysql.RoleRepository + Helicopter *mysql.HelicopterRepository + HelicopterUsage *mysql.HelicopterUsageRepository + FlightInspection *mysql.FlightInspectionRepository + Flight *mysql.FlightRepository + FlightData *mysql.FlightDataRepository + Mission *mysql.MissionRepository + ReserveAc *mysql.ReserveAcRepository + HelicopterFile *mysql.HelicopterFileRepository + FlightInspectionFileChecklist *mysql.FlightInspectionFileChecklistRepository + BeforeFlightInspection *mysql.BeforeFlightInspectionRepository + AfterFlightInspection *mysql.AfterFlightInspectionRepository + FMReport *mysql.FMReportRepository + FlightPrepCheck *mysql.FlightPrepCheckRepository + Facility *mysql.FacilityRepository + Base *mysql.BaseRepository + Medicine *mysql.MedicineRepository + Vocation *mysql.VocationRepository + Opc *mysql.OpcRepository + ForcesPresent *mysql.ForcesPresentRepository + Hospital *mysql.HospitalRepository + HealthInsuranceCompanies *mysql.HealthInsuranceCompaniesRepository + FederalState *mysql.FederalStateRepository + ICAO *mysql.ICAORepository + Land *mysql.LandRepository + Complaint *mysql.ComplaintRepository + EASARelease *mysql.EASAReleaseRepository + ActionSignoff *mysql.ActionSignoffRepository + OtherPerson *mysql.OtherPersonRepository + MCF *mysql.MCFRepository + FleetHistory *mysql.FleetHistoryRepository + HEMSOperationalData *mysql.HEMSOperationalDataRepository + HEMSOperationCategory *mysql.HEMSOperationCategoryRepository + HEMSOperation *mysql.HEMSOperationRepository + MasterSettings *mysql.MasterSettingsRepository + FileManagerFolder *mysql.FileManagerFolderRepository + FileManagerFile *mysql.FileManagerFileRepository + FileManagerAttachment *mysql.FileManagerAttachmentRepository + DutyRoster *mysql.DutyRosterRepository + AirRescuerChecklist *mysql.AirRescuerChecklistRepository + InsurancePatientData *mysql.InsurancePatientDataRepository + PatientData *mysql.PatientDataRepository + DUL *mysql.DULRepository + FleetStatus *mysql.FleetStatusRepository + TokenStore *mysql.TokenStore + Auth *mysql.AuthRepository + Audit *mysql.AuditRepository +} + +type Infrastructure struct { + DB *gorm.DB + Logger *slog.Logger + SQSBreaker *resilience.Executor + FileStorage filemanager.ObjectStorage + EmailQueue service.EmailQueue + FileQueue filemanager.FileProcessingQueue + FileStatusHub *filemanagerrealtime.Hub + HelicopterFileHub *helicopterfilerealtime.Hub + FileTrashPolicy config.FileManagerTrashPolicyConfig + FileLifecycle config.FileManagerLifecycleConfig +} + +type Services struct { + User *service.UserService + Contact *service.ContactService + Role *service.RoleService + Helicopter *service.HelicopterService + HelicopterUsage *service.HelicopterUsageService + FlightInspection *service.FlightInspectionService + Flight *service.FlightService + FlightData *service.FlightDataService + Mission *service.MissionService + ReserveAc *service.ReserveAcService + Takeover *service.TakeoverService + OtherPerson *service.OtherPersonService + HelicopterFile *service.HelicopterFileService + FlightInspectionChecklist *service.FlightInspectionFileChecklistService + BeforeFlightInspection *service.BeforeFlightInspectionService + AfterFlightInspection *service.AfterFlightInspectionService + FMReport *service.FMReportService + FlightPrepCheck *service.FlightPrepCheckService + Facility *service.FacilityService + Base *service.BaseService + Medicine *service.MedicineService + Vocation *service.VocationService + Opc *service.OpcService + ForcesPresent *service.ForcesPresentService + Hospital *service.HospitalService + HealthInsuranceCompanies *service.HealthInsuranceCompaniesService + FederalState *service.FederalStateService + ICAO *service.ICAOService + Land *service.LandService + Complaint *service.ComplaintService + EASARelease *service.EASAReleaseService + ActionSignoff *service.ActionSignoffService + MCF *service.MCFService + FleetHistory *service.FleetHistoryService + HEMSOperationalData *service.HEMSOperationalDataService + HEMSOperationCategory *service.HEMSOperationCategoryService + HEMSOperation *service.HEMSOperationService + MasterSettings *service.MasterSettingsService + FileManager *service.FileManagerService + DutyRoster *service.DutyRosterService + AirRescuerChecklist *service.AirRescuerChecklistService + InsurancePatientData *service.InsurancePatientDataService + PatientData *service.PatientDataService + DUL *service.DULService + FleetStatus *service.FleetStatusService + WOPI *service.WOPIService + Auth *service.AuthService + Audit *service.AuditLogService +} + +type Handlers struct { + User *handlers.UserHandler + Contact *handlers.ContactHandler + Health *handlers.HealthHandler + BuildInfo *handlers.BuildInfoHandler + Auth *handlers.AuthHandler + Role *handlers.RoleHandler + Helicopter *handlers.HelicopterHandler + HelicopterUsage *handlers.HelicopterUsageHandler + HelicopterFile *handlers.HelicopterFileHandler + Flight *handlers.FlightHandler + FlightData *handlers.FlightDataHandler + Mission *handlers.MissionHandler + FMReport *handlers.FMReportHandler + ReserveAc *handlers.ReserveAcHandler + Takeover *handlers.TakeoverHandler + OtherPerson *handlers.OtherPersonHandler + Facility *handlers.FacilityHandler + Base *handlers.BaseHandler + Medicine *handlers.MedicineHandler + Vocation *handlers.VocationHandler + Opc *handlers.OpcHandler + ForcesPresent *handlers.ForcesPresentHandler + Hospital *handlers.HospitalHandler + HealthInsuranceCompanies *handlers.HealthInsuranceCompaniesHandler + FederalState *handlers.FederalStateHandler + ICAO *handlers.ICAOHandler + Land *handlers.LandHandler + MasterSettings *handlers.MasterSettingsHandler + Branding *handlers.BrandingHandler + FileManagerFolder *handlers.FileManagerFolderHandler + FileManagerFile *handlers.FileManagerFileHandler + FileManagerAttachment *handlers.FileManagerAttachmentHandler + FileManagerEvents *handlers.FileManagerEventsHandler + HelicopterFileEvents *handlers.HelicopterFileEventsHandler + DutyRoster *handlers.DutyRosterHandler + AirRescuerChecklist *handlers.AirRescuerChecklistHandler + InsurancePatientData *handlers.InsurancePatientDataHandler + PatientData *handlers.PatientDataHandler + DUL *handlers.DULHandler + FleetStatus *handlers.FleetStatusHandler + HEMSOperationalData *handlers.HEMSOperationalDataHandler + HEMSOperationCategory *handlers.HEMSOperationCategoryHandler + HEMSOperation *handlers.HEMSOperationHandler + Complaint *handlers.ComplaintHandler + EASARelease *handlers.EASAReleaseHandler + ActionSignoff *handlers.ActionSignoffHandler + MCF *handlers.MCFHandler +} + +func initializeDatabase(mysqlCfg config.MySQLConfig) (*gorm.DB, error) { + return mysql.Connect(mysqlCfg) +} + +func bootstrapData(db *gorm.DB) error { + if err := mysql.AutoMigrate(db); err != nil { + return fmt.Errorf("auto migrate: %w", err) + } + if err := db.Exec("UPDATE users SET timezone = ? WHERE timezone IS NULL OR timezone = ''", tzpkg.Default).Error; err != nil { + return fmt.Errorf("seed users timezone default: %w", err) + } + if err := api.SeedRoles(db); err != nil { + return fmt.Errorf("seed roles: %w", err) + } + if err := api.SeedPermissions(db); err != nil { + return fmt.Errorf("seed permissions: %w", err) + } + if err := api.SeedRolePermissions(db); err != nil { + return fmt.Errorf("seed role permissions: %w", err) + } + if err := api.SeedBaseCategories(db); err != nil { + return fmt.Errorf("seed base categories: %w", err) + } + if strings.EqualFold(strings.TrimSpace(os.Getenv("SEED_ROSTER_USERS")), "true") { + if err := api.SeedRosterUsers(db); err != nil { + return fmt.Errorf("seed roster users: %w", err) + } + } + return nil +} + +func initializeRepositories(db *gorm.DB) (*Repositories, error) { + if db == nil { + return nil, errors.New("database is required") + } + repos := &Repositories{ + User: mysql.NewUserRepository(db), + Contact: mysql.NewContactRepository(db), + Role: mysql.NewRoleRepository(db), + Helicopter: mysql.NewHelicopterRepository(db), + HelicopterUsage: mysql.NewHelicopterUsageRepository(db), + FlightInspection: mysql.NewFlightInspectionRepository(db), + Flight: mysql.NewFlightRepository(db), + FlightData: mysql.NewFlightDataRepository(db), + Mission: mysql.NewMissionRepository(db), + ReserveAc: mysql.NewReserveAcRepository(db), + HelicopterFile: mysql.NewHelicopterFileRepository(db), + FlightInspectionFileChecklist: mysql.NewFlightInspectionFileChecklistRepository(db), + BeforeFlightInspection: mysql.NewBeforeFlightInspectionRepository(db), + AfterFlightInspection: mysql.NewAfterFlightInspectionRepository(db), + FMReport: mysql.NewFMReportRepository(db), + FlightPrepCheck: mysql.NewFlightPrepCheckRepository(db), + Facility: mysql.NewFacilityRepository(db), + Base: mysql.NewBaseRepository(db), + Medicine: mysql.NewMedicineRepository(db), + Vocation: mysql.NewVocationRepository(db), + Opc: mysql.NewOpcRepository(db), + ForcesPresent: mysql.NewForcesPresentRepository(db), + Hospital: mysql.NewHospitalRepository(db), + HealthInsuranceCompanies: mysql.NewHealthInsuranceCompaniesRepository(db), + FederalState: mysql.NewFederalStateRepository(db), + ICAO: mysql.NewICAORepository(db), + Land: mysql.NewLandRepository(db), + Complaint: mysql.NewComplaintRepository(db), + EASARelease: mysql.NewEASAReleaseRepository(db), + ActionSignoff: mysql.NewActionSignoffRepository(db), + OtherPerson: mysql.NewOtherPersonRepository(db), + MCF: mysql.NewMCFRepository(db), + FleetHistory: mysql.NewFleetHistoryRepository(db), + HEMSOperationalData: mysql.NewHEMSOperationalDataRepository(db), + HEMSOperationCategory: mysql.NewHEMSOperationCategoryRepository(db), + HEMSOperation: mysql.NewHEMSOperationRepository(db), + MasterSettings: mysql.NewMasterSettingsRepository(db), + FileManagerFolder: mysql.NewFileManagerFolderRepository(db), + FileManagerFile: mysql.NewFileManagerFileRepository(db), + FileManagerAttachment: mysql.NewFileManagerAttachmentRepository(db), + DutyRoster: mysql.NewDutyRosterRepository(db), + AirRescuerChecklist: mysql.NewAirRescuerChecklistRepository(db), + InsurancePatientData: mysql.NewInsurancePatientDataRepository(db), + PatientData: mysql.NewPatientDataRepository(db), + DUL: mysql.NewDULRepository(db), + FleetStatus: mysql.NewFleetStatusRepository(db), + TokenStore: mysql.NewTokenStore(db), + Auth: mysql.NewAuthRepository(db), + Audit: mysql.NewAuditRepository(db), + } + return repos, nil +} + +func initializeInfrastructure(ctx context.Context, appCfg config.AppConfig, db *gorm.DB) (*Infrastructure, error) { + appLogger := pkglogger.New(appCfg.Logging).With("component", "api") + slog.SetDefault(appLogger) + + if err := config.ValidateDistinctQueueURLs(appCfg.SQS, appCfg.FileSQS); err != nil { + return nil, err + } + + resCfg := appCfg.Resilience + sqsBreaker := resilience.NewExecutor("sqs", resCfg.SQS, appLogger, resilience.ClassifySQSError) + + var emailQueue service.EmailQueue + if !appCfg.Queue.OutboxEnabled { + emailProducer, err := queueapp.NewEmailProducer(ctx, appCfg.Queue, appCfg.SQS, sqsBreaker, appLogger) + if err != nil { + return nil, err + } + emailQueue = service.AdaptEmailQueue(emailProducer) + } + + var fileStorage filemanager.ObjectStorage + if appCfg.FileStorage.Bucket != "" && appCfg.FileStorage.Region != "" { + storage, err := s3repo.NewFileObjectStorage(ctx, appCfg.FileStorage) + if err != nil { + return nil, err + } + fileStorage = storage + } + + var fileQueue filemanager.FileProcessingQueue + if !appCfg.FileQueue.OutboxEnabled && strings.TrimSpace(appCfg.FileSQS.QueueURL) != "" { + fileProducer, err := queueapp.NewFileProcessingProducer(ctx, appCfg.FileQueue, appCfg.FileSQS, sqsBreaker, appLogger) + if err != nil { + return nil, err + } + fileQueue = service.AdaptFileProcessingQueue(fileProducer) + } + + return &Infrastructure{ + DB: db, + Logger: appLogger, + SQSBreaker: sqsBreaker, + FileStorage: fileStorage, + EmailQueue: emailQueue, + FileQueue: fileQueue, + FileStatusHub: filemanagerrealtime.NewHub(64, appLogger), + HelicopterFileHub: helicopterfilerealtime.NewHub(64, appLogger), + FileTrashPolicy: config.LoadFileManagerTrashPolicyConfigFromEnv(), + FileLifecycle: config.LoadFileManagerLifecycleConfigFromEnv(), + }, nil +} + +func initializeServices(appCfg config.AppConfig, repos *Repositories, infra *Infrastructure) (*Services, error) { + if repos == nil { + return nil, errors.New("repositories are required") + } + if infra == nil { + return nil, errors.New("infrastructure is required") + } + + var protector service.SecretProtector + if p, err := service.NewAESGCMProtectorFromBase64(appCfg.Auth.TOTPSecretKeyB64); err == nil { + protector = p + } + + fileManagerDeps := service.FileManagerServiceDependencies{ + Storage: infra.FileStorage, + FileQueue: infra.FileQueue, + UploadPolicy: filemanager.FileUploadPolicy{}, + TrashRetention: infra.FileTrashPolicy.Retention, + FileStatusEvents: repos.FileManagerFile, + Logger: infra.Logger, + EnableS3Tagging: infra.FileLifecycle.EnableS3Tagging, + } + if appCfg.FileQueue.OutboxEnabled { + fileManagerDeps.FileOutbox = repos.FileManagerFile + fileManagerDeps.FileOutboxSerializer = queueapp.NewFileProcessingSerializer(appCfg.FileQueue, appCfg.FileSQS) + } + fileManagerDeps.UploadPolicy = filemanager.FileUploadPolicy{ + MaxFileSizeBytes: appCfg.FileUpload.MaxFileSizeBytes, + AllowedMimeTypes: appCfg.FileUpload.AllowedMimeTypes, + AllowedExtensions: appCfg.FileUpload.AllowedExtensions, + AllowEmptyFile: appCfg.FileUpload.AllowEmptyFile, + MaxFilenameLength: appCfg.FileUpload.MaxFilenameLength, + } + + flightDataSvc := service.NewFlightDataService(repos.FlightData) + flightSvc := service.NewFlightService(repos.Flight) + fileManagerSvc := service.NewFileManagerService(repos.FileManagerFolder, repos.FileManagerFile, repos.FileManagerAttachment, fileManagerDeps) + helicopterSvc := service.NewHelicopterService(repos.Helicopter) + helicopterUsageSvc := service.NewHelicopterUsageService(repos.HelicopterUsage) + afterFlightInspectionSvc := service.NewAfterFlightInspectionService(repos.AfterFlightInspection) + + services := &Services{ + User: service.NewUserService(repos.User), + Role: service.NewRoleService(repos.Role), + Helicopter: helicopterSvc, + HelicopterUsage: helicopterUsageSvc, + FlightInspection: service.NewFlightInspectionService(repos.FlightInspection), + Flight: flightSvc, + FlightData: flightDataSvc, + Mission: nil, + ReserveAc: service.NewReserveAcService(repos.ReserveAc), + Takeover: service.NewTakeoverService(infra.DB), + HelicopterFile: service.NewHelicopterFileService(repos.HelicopterFile), + FlightInspectionChecklist: service.NewFlightInspectionFileChecklistService(repos.FlightInspectionFileChecklist), + BeforeFlightInspection: service.NewBeforeFlightInspectionService(repos.BeforeFlightInspection), + AfterFlightInspection: afterFlightInspectionSvc, + FMReport: service.NewFMReportService(repos.FMReport), + FlightPrepCheck: service.NewFlightPrepCheckService(repos.FlightPrepCheck, repos.FlightInspection), + Facility: service.NewFacilityService(repos.Facility), + Base: service.NewBaseService(repos.Base), + Medicine: service.NewMedicineService(repos.Medicine), + Vocation: service.NewVocationService(repos.Vocation), + Opc: service.NewOpcService(repos.Opc), + ForcesPresent: service.NewForcesPresentService(repos.ForcesPresent), + Hospital: service.NewHospitalService(repos.Hospital), + HealthInsuranceCompanies: service.NewHealthInsuranceCompaniesService(repos.HealthInsuranceCompanies), + FederalState: service.NewFederalStateService(repos.FederalState), + ICAO: service.NewICAOService(repos.ICAO), + Land: service.NewLandService(repos.Land), + Complaint: service.NewComplaintService(repos.Complaint), + EASARelease: service.NewEASAReleaseService(repos.EASARelease), + ActionSignoff: service.NewActionSignoffService(repos.ActionSignoff), + OtherPerson: service.NewOtherPersonService(repos.OtherPerson), + MCF: service.NewMCFService(repos.MCF), + FleetHistory: service.NewFleetHistoryService(repos.FleetHistory), + HEMSOperationalData: service.NewHEMSOperationalDataService(repos.HEMSOperationalData), + HEMSOperationCategory: service.NewHEMSOperationCategoryService(repos.HEMSOperationCategory), + HEMSOperation: service.NewHEMSOperationService(repos.HEMSOperation), + MasterSettings: service.NewMasterSettingsService(repos.MasterSettings, service.MasterSettingsServiceDependencies{Protector: protector}), + FileManager: fileManagerSvc, + DutyRoster: service.NewDutyRosterService(repos.DutyRoster), + AirRescuerChecklist: service.NewAirRescuerChecklistService(repos.AirRescuerChecklist), + InsurancePatientData: service.NewInsurancePatientDataService(repos.InsurancePatientData), + PatientData: service.NewPatientDataService(repos.PatientData), + DUL: service.NewDULService(repos.DUL), + Audit: service.NewAuditLogService(repos.Audit, service.AuditLogServiceDependencies{QueueSize: appCfg.Audit.QueueSize, Workers: appCfg.Audit.Workers, IPEncryptionKey: appCfg.Auth.IPEncryptionKey}), + WOPI: service.NewWOPIService(service.WOPIConfig{ + TokenSecret: appCfg.WOPI.TokenSecret, + ProofSecret: appCfg.WOPI.ProofSecret, + DiscoveryURL: appCfg.WOPI.DiscoveryURL, + PublicBaseURL: appCfg.WOPI.PublicBaseURL, + EditActionURL: appCfg.WOPI.EditActionURL, + TokenTTL: appCfg.WOPI.TokenTTL, + MaxBodyBytes: appCfg.WOPI.MaxBodyBytes, + RequireProof: appCfg.WOPI.RequireProof, + }), + } + services.Mission = service.NewMissionService(repos.Mission, flightSvc, flightDataSvc, afterFlightInspectionSvc) + services.FleetStatus = service.NewFleetStatusService(repos.FleetStatus, fileManagerSvc, helicopterSvc, services.Complaint).WithHistory(services.FleetHistory).WithUsage(services.HelicopterUsage) + flightDataSvc.WithAOGRecalculator(services.FleetStatus) + flightDataSvc.WithMissionResolver(services.Mission) + flightDataSvc.WithFlightLookup(services.Flight) + flightDataSvc.WithFMReportLookup(services.FMReport) + flightDataSvc.WithAfterFlightInspectionLookup(services.AfterFlightInspection) + services.FMReport.WithUsageRefresher(services.HelicopterUsage) + + msSSO := service.NewMicrosoftSSO(appCfg.MicrosoftSSO). + WithExecutor(resilience.NewExecutor("microsoft_sso", appCfg.Resilience.MicrosoftSSO, infra.Logger, resilience.ClassifyMicrosoftSSOError)). + WithConfigResolver(func(ctx context.Context) (config.MicrosoftSSOConfig, error) { + cfg, err := services.MasterSettings.LoadMicrosoftEntraRuntimeConfig(ctx) + if err == nil { + return cfg, nil + } + // Fallback for local/dev when master settings are missing or stale. + // This prevents SSO flows from failing hard when DB-stored settings are invalid. + if service.IsMicrosoftSSOConfigComplete(appCfg.MicrosoftSSO) { + if infra.Logger != nil { + infra.Logger.Warn("microsoft sso runtime config fallback to env", "reason", err.Error()) + } + return appCfg.MicrosoftSSO, nil + } + return config.MicrosoftSSOConfig{}, err + }) + + authDeps := service.AuthServiceDependencies{ + SSO: msSSO, + Protector: protector, + Config: appCfg.Auth, + Audit: services.Audit, + EmailBranding: service.NewAuthEmailBrandingResolver(services.MasterSettings, services.FileManager, infra.FileStorage), + EmailOTPEnabled: services.MasterSettings.IsLoginEmailOTPEnabled, + DisableLegacyNoopEmailQueue: true, + } + if appCfg.Queue.OutboxEnabled { + authDeps.EmailOutbox = repos.Auth + authDeps.EmailSerializer = queueapp.NewOutboxSerializer(appCfg.Queue, appCfg.SQS) + } + services.Auth = service.NewAuthService( + repos.Auth, + repos.Role, + repos.TokenStore, + infra.EmailQueue, + authDeps, + ) + + services.Contact = service.NewContactService(repos.Contact, services.Auth) + services.HelicopterFile. + WithFileManagerService(services.FileManager). + WithHelicopterFileEvents(infra.HelicopterFileHub) + if infra.FileStorage != nil && !appCfg.FileQueue.OutboxEnabled && strings.TrimSpace(appCfg.FileSQS.QueueURL) == "" { + return nil, errors.New("FILE_SQS_QUEUE_URL is required when FILE_QUEUE_OUTBOX_ENABLED=false and file manager upload is enabled") + } + + return services, nil +} + +func initializeHandlers(services *Services, infra *Infrastructure) (*Handlers, error) { + if services == nil { + return nil, errors.New("services are required") + } + if infra == nil { + return nil, errors.New("infrastructure is required") + } + + fleetStatusHandler := handlers.NewFleetStatusHandler(services.FleetStatus, handlers.FleetStatusDeps{ + Helicopter: services.Helicopter, + Complaint: services.Complaint, + EASA: services.EASARelease, + History: services.FleetHistory, + MCF: services.MCF, + Signoff: services.ActionSignoff, + Storage: infra.FileStorage, + }) + + fmReportFlightHandler := handlers.NewFlightHandler(services.Flight). + WithAuthService(services.Auth). + WithAfterFlightInspectionService(services.AfterFlightInspection). + WithFMReportService(services.FMReport). + WithFlightDataService(services.FlightData). + WithTakeoverService(service.NewTakeoverModuleService(mysql.NewTakeoverRepository(infra.DB))). + WithHelicopterService(services.Helicopter). + WithReserveAcService(services.ReserveAc). + WithMissionService(services.Mission) + + services.FMReport.WithFleetHistoryBuilder(handlers.NewFMReportFleetHistoryBuilder(fleetStatusHandler, fmReportFlightHandler)) + services.FMReport.WithReportCodeResolver(fmReportFlightHandler) + + return &Handlers{ + User: handlers.NewUserHandler(services.User). + WithFileManagerService(services.FileManager). + WithFileStorage(infra.FileStorage), + Contact: handlers.NewContactHandler(services.Contact, services.Auth). + WithFileManagerService(services.FileManager). + WithFileStorage(infra.FileStorage), + Health: handlers.NewHealthHandler(), + BuildInfo: handlers.NewBuildInfoHandler(), + Auth: handlers.NewAuthHandler(services.Auth).WithFileStorage(infra.FileStorage).WithContactService(services.Contact), + Role: handlers.NewRoleHandler(services.Role), + Helicopter: handlers.NewHelicopterHandler(services.Helicopter). + WithFileManagerService(services.FileManager). + WithFileStorage(infra.FileStorage). + WithReserveAcService(services.ReserveAc). + WithFleetStatusService(services.FleetStatus). + WithPINVerifier(services.Auth). + WithComplaintService(services.Complaint). + WithMCFService(services.MCF). + WithHistoryService(services.FleetHistory), + HelicopterUsage: handlers.NewHelicopterUsageHandler(services.HelicopterUsage), + HelicopterFile: handlers.NewHelicopterFileHandler(services.HelicopterFile). + WithFileManagerService(services.FileManager). + WithHelicopterService(services.Helicopter). + WithTakeoverService(services.Takeover). + WithHelicopterFileEvents(infra.HelicopterFileHub). + WithFileStorage(infra.FileStorage), + Flight: handlers.NewFlightHandler(services.Flight). + WithAuthService(services.Auth). + WithAfterFlightInspectionService(services.AfterFlightInspection). + WithFMReportService(services.FMReport). + WithFlightDataService(services.FlightData). + WithTakeoverService(service.NewTakeoverModuleService(mysql.NewTakeoverRepository(infra.DB))). + WithHelicopterService(services.Helicopter). + WithReserveAcService(services.ReserveAc). + WithMissionService(services.Mission), + FlightData: handlers.NewFlightDataHandler(services.FlightData). + WithMissionService(services.Mission). + WithUserService(services.User). + WithHospitalService(services.Hospital). + WithICAOService(services.ICAO). + WithFacilityService(services.Facility), + Mission: handlers.NewMissionHandler(services.Mission).WithFlightDataService(services.FlightData).WithFileStorage(infra.FileStorage).WithFileManagerService(services.FileManager), + FMReport: handlers.NewFMReportHandler(services.FMReport). + WithFlightHandler(fmReportFlightHandler). + WithMissionHandler(handlers.NewMissionHandler(services.Mission).WithFlightDataService(services.FlightData).WithFileStorage(infra.FileStorage).WithFileManagerService(services.FileManager)). + WithFlightInspectionService(services.FlightInspection). + WithAfterFlightInspectionService(services.AfterFlightInspection). + WithHelicopterUsageService(services.HelicopterUsage). + WithFleetStatusHandler(fleetStatusHandler). + WithTakeoverService(services.Takeover). + WithHelicopterService(services.Helicopter). + WithFileStorage(infra.FileStorage). + WithFileManagerService(services.FileManager), + ReserveAc: handlers.NewReserveAcHandler(services.ReserveAc). + WithFlightService(services.Flight). + WithFlightDataService(services.FlightData). + WithMissionService(services.Mission). + WithDutyRosterService(services.DutyRoster). + WithHelicopterService(services.Helicopter). + WithFlightInspectionService(services.FlightInspection). + WithBeforeFlightInspectionService(services.BeforeFlightInspection). + WithAfterFlightInspectionService(services.AfterFlightInspection). + WithFMReportService(services.FMReport). + WithFlightPrepCheckService(services.FlightPrepCheck). + WithHelicopterFileService(services.HelicopterFile). + WithFlightInspectionFileChecklistService(services.FlightInspectionChecklist). + WithFileStorage(infra.FileStorage), + Takeover: handlers.NewTakeoverHandler(infra.DB). + WithAuthService(services.Auth). + WithBaseService(services.Base). + WithFlightService(services.Flight). + WithFileManagerService(services.FileManager). + WithTakeoverAcService(service.NewTakeoverModuleService(mysql.NewTakeoverRepository(infra.DB))). + WithReserveAcService(services.ReserveAc). + WithDutyRosterService(services.DutyRoster). + WithHelicopterService(services.Helicopter). + WithComplaintService(services.Complaint). + WithMCFService(services.MCF). + WithFleetStatusService(services.FleetStatus). + WithFlightInspectionService(services.FlightInspection). + WithBeforeFlightInspectionService(services.BeforeFlightInspection). + WithFlightPrepCheckService(services.FlightPrepCheck). + WithHelicopterFileService(services.HelicopterFile). + WithFlightInspectionFileChecklistService(services.FlightInspectionChecklist). + WithFileStorage(infra.FileStorage), + OtherPerson: handlers.NewOtherPersonHandler(services.OtherPerson), + Facility: handlers.NewFacilityHandler(services.Facility), + Base: handlers.NewBaseHandler(services.Base).WithFileManagerService(services.FileManager).WithFileStorage(infra.FileStorage), + Medicine: handlers.NewMedicineHandler(services.Medicine), + Vocation: handlers.NewVocationHandler(services.Vocation), + Opc: handlers.NewOpcHandler(services.Opc), + ForcesPresent: handlers.NewForcesPresentHandler(services.ForcesPresent), + Hospital: handlers.NewHospitalHandler(services.Hospital), + HealthInsuranceCompanies: handlers.NewHealthInsuranceCompaniesHandler(services.HealthInsuranceCompanies), + FederalState: handlers.NewFederalStateHandler(services.FederalState), + ICAO: handlers.NewICAOHandler(services.ICAO), + Land: handlers.NewLandHandler(services.Land), + MasterSettings: handlers.NewMasterSettingsHandler(services.MasterSettings, services.FileManager, infra.FileStorage), + Branding: handlers.NewBrandingHandler(services.MasterSettings, services.FileManager, infra.FileStorage), + FileManagerFolder: handlers.NewFileManagerFolderHandler(services.FileManager, infra.FileStorage), + FileManagerFile: handlers.NewFileManagerFileHandler(services.FileManager, infra.FileStorage). + WithHelicopterFileService(services.HelicopterFile). + WithAircraftService(services.Helicopter). + WithBaseService(services.Base). + WithDULService(services.DUL). + WithAuthService(services.Auth). + WithTakeoverService(services.Takeover). + WithMissionService(services.Mission). + WithUserService(services.User). + WithFleetStatusFileService(service.NewFleetStatusFileService(services.FileManager)). + WithHelicopterFileEvents(infra.HelicopterFileHub). + WithWOPIService(services.WOPI), + FileManagerAttachment: handlers.NewFileManagerAttachmentHandler(services.FileManager), + FileManagerEvents: handlers.NewFileManagerEventsHandler(infra.FileStatusHub), + HelicopterFileEvents: handlers.NewHelicopterFileEventsHandler(infra.HelicopterFileHub), + DutyRoster: handlers.NewDutyRosterHandler(services.DutyRoster).WithAudit(services.Audit).WithFlightService(services.Flight), + AirRescuerChecklist: handlers.NewAirRescuerChecklistHandler(services.AirRescuerChecklist), + InsurancePatientData: handlers.NewInsurancePatientDataHandler(services.InsurancePatientData), + PatientData: handlers.NewPatientDataHandler(services.PatientData), + DUL: handlers.NewDULHandler(services.DUL).WithBaseService(services.Base).WithContactService(services.Contact).WithFileManagerService(services.FileManager).WithFileStorage(infra.FileStorage), + FleetStatus: fleetStatusHandler, + HEMSOperationalData: handlers.NewHEMSOperationalDataHandler(services.HEMSOperationalData).WithFileManagerService(services.FileManager), + HEMSOperationCategory: handlers.NewHEMSOperationCategoryHandler(services.HEMSOperationCategory), + HEMSOperation: handlers.NewHEMSOperationHandler(services.HEMSOperation), + Complaint: handlers.NewComplaintHandler(services.Complaint).WithHelicopterService(services.Helicopter).WithMCFService(services.MCF).WithHistoryService(services.FleetHistory).WithAOGRecalculator(services.FleetStatus).WithActionSignoffService(services.ActionSignoff), + EASARelease: handlers.NewEASAReleaseHandler(services.EASARelease).WithComplaintActionChecker(services.Complaint).WithActionSignoffChecker(services.ActionSignoff).WithHistoryService(services.FleetHistory).WithContactResolver(services.Contact).WithAOGRecalculator(services.FleetStatus), + ActionSignoff: handlers.NewActionSignoffHandler(services.ActionSignoff).WithComplaintService(services.Complaint), + MCF: handlers.NewMCFHandler(services.MCF).WithHistoryService(services.FleetHistory), + }, nil +} + +func initializeRealtimeComponents( + ctx context.Context, + repos *Repositories, + services *Services, + infra *Infrastructure, +) error { + if repos == nil || services == nil || infra == nil { + return errors.New("repositories, services, and infrastructure are required") + } + userNameCache := userctx.NewUserNameCache(repos.User, 5*time.Minute) + handlers.WireUserNameGetter(userNameCache) + + startFileManagerAutoPurgeLoop(ctx, services.FileManager, infra.FileTrashPolicy, infra.Logger) + startFileManagerLifecycleCleanupLoop(ctx, services.FileManager, infra.FileLifecycle, infra.Logger) + startHelicopterFileAttachmentReconcileLoop(ctx, services.HelicopterFile, infra.Logger) + startFleetStatusAOGRecomputeLoop(ctx, services.FleetStatus, infra.Logger) + + fileStatusPoller := filemanagerrealtime.NewStatusPoller(infra.DB, infra.FileStatusHub, infra.Logger, filemanagerrealtime.StatusPollerConfig{ + // Keep the SSE ready event responsive; this poller is the bridge between + // the async file processor and the live file.updated stream. + PollInterval: 250 * time.Millisecond, + BatchSize: 100, + OnPublished: func(ctx context.Context, event filemanagerrealtime.FileStatusEvent) error { + if filemanager.NormalizeFileStatus(event.Status) != filemanager.FileStatusReady { + return nil + } + if services == nil || services.HelicopterFile == nil { + return nil + } + finalized, err := services.HelicopterFile.FinalizePendingAttachmentsBySourceFileUUID(ctx, event.FileUUID, 50) + if err != nil { + return err + } + if finalized > 0 && infra.Logger != nil { + infra.Logger.Info("helicopter file attachments finalized from file ready event", "file_uuid", event.FileUUID, "finalized", finalized) + } + return nil + }, + }) + go func() { + if err := fileStatusPoller.Run(ctx); err != nil && ctx.Err() == nil { + infra.Logger.Error("file status poller stopped unexpectedly", "error", err) + } + }() + + return nil +} + +func buildHTTPServer(appCfg config.AppConfig, services *Services, handlersSet *Handlers, infra *Infrastructure) (*fiber.App, string, error) { + if services == nil || handlersSet == nil || infra == nil { + return nil, "", errors.New("services, handlers, and infra are required") + } + httpCfg := appCfg.HTTP + app := fiber.New(fiber.Config{ + BodyLimit: httpCfg.BodyLimit, + ReadTimeout: httpCfg.ReadTimeout, + WriteTimeout: httpCfg.WriteTimeout, + IdleTimeout: httpCfg.IdleTimeout, + }) + api.UseDefaultMiddlewares(app, httpCfg) + + envCfg := config.LoadEnvConfig() + hasJSONSAML := appCfg.SAML.CertFile != "" || + appCfg.SAML.KeyFile != "" || + appCfg.SAML.MetadataFile != "" || + appCfg.SAML.RootURL != "" || + appCfg.SAML.FrontendURL != "" || + appCfg.SAML.CookieDomain != "" || + appCfg.SAML.SessionTTLHr > 0 + if appCfg.SAML.CertFile != "" { + envCfg.SAMLCertFile = appCfg.SAML.CertFile + } + if appCfg.SAML.KeyFile != "" { + envCfg.SAMLKeyFile = appCfg.SAML.KeyFile + } + if appCfg.SAML.MetadataFile != "" { + envCfg.SAMLMetadataFile = appCfg.SAML.MetadataFile + } + if appCfg.SAML.RootURL != "" { + envCfg.SAMLRootURL = appCfg.SAML.RootURL + } + if appCfg.SAML.FrontendURL != "" { + envCfg.FrontendURL = appCfg.SAML.FrontendURL + } + if appCfg.SAML.CookieDomain != "" { + envCfg.CookieDomain = appCfg.SAML.CookieDomain + } + if hasJSONSAML { + envCfg.CookieSecure = appCfg.SAML.CookieSecure + } + if appCfg.SAML.SessionTTLHr > 0 { + envCfg.SessionTTLHour = appCfg.SAML.SessionTTLHr + } + + // Microsoft auth mode selector. Default "oauth" uses the OIDC/OAuth2 authorization + // code flow (Microsoft refresh tokens are stored in DB and validated on + // /api/v1/auth/refresh). Set MICROSOFT_AUTH_MODE=saml to fall back to the legacy + // SAML flow (kept intact for rollback). + microsoftAuthMode := strings.ToLower(strings.TrimSpace(os.Getenv("MICROSOFT_AUTH_MODE"))) + if microsoftAuthMode == "" { + microsoftAuthMode = "oauth" + } + + var samlHandler *samlauth.Handler + if microsoftAuthMode == "saml" { + if err := samlauth.InitSessionStoreDB(infra.DB); err == nil { + if err := samlauth.InitSAML(envCfg); err == nil { + samlHandler = samlauth.NewHandler(envCfg, services.Auth) + } + } + } + slog.Info("microsoft auth mode configured", slog.String("mode", microsoftAuthMode), slog.Bool("saml_active", samlHandler != nil)) + + api.RegisterRoutes(app, api.RouteDependencies{ + Handlers: api.RouteHandlers{ + User: handlersSet.User, + Contact: handlersSet.Contact, + Health: handlersSet.Health, + BuildInfo: handlersSet.BuildInfo, + Auth: handlersSet.Auth, + Role: handlersSet.Role, + Helicopter: handlersSet.Helicopter, + HelicopterUsage: handlersSet.HelicopterUsage, + HelicopterFile: handlersSet.HelicopterFile, + Flight: handlersSet.Flight, + FlightData: handlersSet.FlightData, + Mission: handlersSet.Mission, + FMReport: handlersSet.FMReport, + ReserveAc: handlersSet.ReserveAc, + Takeover: handlersSet.Takeover, + OtherPerson: handlersSet.OtherPerson, + Facility: handlersSet.Facility, + Base: handlersSet.Base, + Medicine: handlersSet.Medicine, + Vocation: handlersSet.Vocation, + Opc: handlersSet.Opc, + ForcesPresent: handlersSet.ForcesPresent, + Hospital: handlersSet.Hospital, + HealthInsuranceCompanies: handlersSet.HealthInsuranceCompanies, + FederalState: handlersSet.FederalState, + ICAO: handlersSet.ICAO, + Land: handlersSet.Land, + MasterSettings: handlersSet.MasterSettings, + Branding: handlersSet.Branding, + FileManagerFolder: handlersSet.FileManagerFolder, + FileManagerFile: handlersSet.FileManagerFile, + FileManagerAttachment: handlersSet.FileManagerAttachment, + FileManagerEvents: handlersSet.FileManagerEvents, + HelicopterFileEvents: handlersSet.HelicopterFileEvents, + DutyRoster: handlersSet.DutyRoster, + AirRescuerChecklist: handlersSet.AirRescuerChecklist, + InsurancePatientData: handlersSet.InsurancePatientData, + PatientData: handlersSet.PatientData, + DUL: handlersSet.DUL, + FleetStatus: handlersSet.FleetStatus, + HEMSOperationalData: handlersSet.HEMSOperationalData, + HEMSOperationCategory: handlersSet.HEMSOperationCategory, + HEMSOperation: handlersSet.HEMSOperation, + Complaint: handlersSet.Complaint, + EASARelease: handlersSet.EASARelease, + ActionSignoff: handlersSet.ActionSignoff, + MCF: handlersSet.MCF, + SAMLAuth: samlHandler, + }, + Services: api.RouteServices{ + Auth: services.Auth, + Audit: services.Audit, + WOPI: services.WOPI, + }, + }) + + addr := ":8080" + if appCfg.HTTPPort > 0 { + addr = fmt.Sprintf(":%d", appCfg.HTTPPort) + } + return app, addr, nil +} diff --git a/cmd/file-lifecycle-backfill/main.go b/cmd/file-lifecycle-backfill/main.go new file mode 100644 index 0000000..fd33dc0 --- /dev/null +++ b/cmd/file-lifecycle-backfill/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "time" + + "wucher/internal/config" + filemanager "wucher/internal/domain/file_manager" + "wucher/internal/repository/mysql" + s3repo "wucher/internal/repository/s3" +) + +type backfillReport struct { + Total int `json:"total"` + ActiveFiles int `json:"active_files"` + ProbableOrphans int `json:"probable_orphan_files"` + MissingS3Key int `json:"missing_s3_key"` + InvalidReference int `json:"invalid_references"` + Skipped int `json:"skipped"` + DryRun bool `json:"dry_run"` +} + +func main() { + ctx := context.Background() + cfg, err := config.LoadFromFile(configFilePath()) + if err != nil { + fmt.Fprintf(os.Stderr, "load config: %v\n", err) + os.Exit(1) + } + db, err := mysql.Connect(cfg.MySQL) + if err != nil { + fmt.Fprintf(os.Stderr, "connect db: %v\n", err) + os.Exit(1) + } + + fileRepo := mysql.NewFileManagerFileRepository(db) + lifecycleRepo, ok := any(fileRepo).(filemanager.FileLifecycleRepository) + if !ok { + fmt.Fprintln(os.Stderr, "file repository does not support lifecycle operations") + os.Exit(1) + } + + dryRun := true + if raw := os.Getenv("BACKFILL_DRY_RUN"); raw != "" && (raw == "false" || raw == "0") { + dryRun = false + } + + var storage filemanager.ObjectStorage + if cfg.FileStorage.Bucket != "" && cfg.FileStorage.Region != "" { + objStorage, s3Err := s3repo.NewFileObjectStorage(ctx, cfg.FileStorage) + if s3Err == nil { + storage = objStorage + } + } + + rows := make([]filemanager.File, 0) + if err := db.WithContext(ctx).Model(&filemanager.File{}).Find(&rows).Error; err != nil { + fmt.Fprintf(os.Stderr, "query files: %v\n", err) + os.Exit(1) + } + + report := backfillReport{DryRun: dryRun} + now := time.Now().UTC() + for i := range rows { + row := rows[i] + report.Total++ + if row.DeletedAt != nil { + report.Skipped++ + continue + } + + referenced, refErr := lifecycleRepo.IsFileReferenced(ctx, row.ID) + if refErr != nil { + report.InvalidReference++ + report.Skipped++ + continue + } + + if storage != nil { + if _, headErr := storage.HeadObject(ctx, row.ObjectKey); headErr != nil { + report.MissingS3Key++ + } + } + + if referenced { + report.ActiveFiles++ + if !dryRun { + _ = lifecycleRepo.UpdateLifecycle(ctx, filemanager.FileLifecycleTransitionParams{ + ID: row.ID, + Status: filemanager.FileLifecycleStatusActive, + AttachedAt: &now, + ClearOrphanedAt: true, + }) + } + continue + } + + report.ProbableOrphans++ + if !dryRun { + _ = lifecycleRepo.UpdateLifecycle(ctx, filemanager.FileLifecycleTransitionParams{ + ID: row.ID, + Status: filemanager.FileLifecycleStatusOrphanPending, + OrphanedAt: &now, + }) + } + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + _ = enc.Encode(report) +} + +func configFilePath() string { + if v := os.Getenv("CONFIG_FILE"); v != "" { + return v + } + basePath := os.Getenv("CONFIG_BASE_PATH") + if basePath == "" { + basePath = "/tmp" + } + filename := os.Getenv("CONFIG_FILENAME") + if filename == "" { + filename = "config.generated.json" + } + return basePath + "/" + filename +} diff --git a/cmd/healthcheck/main.go b/cmd/healthcheck/main.go new file mode 100644 index 0000000..b943678 --- /dev/null +++ b/cmd/healthcheck/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "bufio" + "net" + "os" + "strings" + "time" +) + +const ( + defaultTarget = "http://127.0.0.1:8080/health" + timeout = 2 * time.Second +) + +func main() { + target := strings.TrimSpace(os.Getenv("HEALTHCHECK_URL")) + if target == "" { + target = defaultTarget + } + if len(os.Args) > 1 { + if arg := strings.TrimSpace(os.Args[1]); arg != "" { + target = arg + } + } + + addr, path, ok := splitTarget(target) + if !ok { + os.Exit(1) + } + + conn, err := net.DialTimeout("tcp", addr, timeout) + if err != nil { + os.Exit(1) + } + defer conn.Close() + + _ = conn.SetDeadline(time.Now().Add(timeout)) + request := "GET " + path + " HTTP/1.1\r\nHost: " + addr + "\r\nConnection: close\r\n\r\n" + if _, err := conn.Write([]byte(request)); err != nil { + os.Exit(1) + } + + line, err := bufio.NewReader(conn).ReadString('\n') + if err != nil { + os.Exit(1) + } + + fields := strings.Fields(line) + if len(fields) < 2 { + os.Exit(1) + } + + code := fields[1] + if len(code) != 3 || (code[0] != '2' && code[0] != '3') { + os.Exit(1) + } +} + +func splitTarget(target string) (string, string, bool) { + target = strings.TrimSpace(target) + if target == "" || strings.HasPrefix(target, "https://") { + return "", "", false + } + + target = strings.TrimPrefix(target, "http://") + addr := target + path := "/" + if slash := strings.IndexByte(target, '/'); slash >= 0 { + addr = target[:slash] + path = target[slash:] + } + + if addr == "" || !strings.Contains(addr, ":") { + return "", "", false + } + + return addr, path, true +} diff --git a/cmd/worker/file_worker.go b/cmd/worker/file_worker.go new file mode 100644 index 0000000..30d5f08 --- /dev/null +++ b/cmd/worker/file_worker.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + "errors" + "os/signal" + "strings" + "syscall" + + queueapp "wucher/internal/app/queueing" + appworker "wucher/internal/app/worker" + "wucher/internal/config" + "wucher/internal/queue" + mysqlrepo "wucher/internal/repository/mysql" + s3repo "wucher/internal/repository/s3" + "wucher/internal/resilience" + "wucher/internal/service" + "wucher/internal/shared/pkg/logger" +) + +func runFileWorker(appCfg config.AppConfig) error { + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + appLogger := logger.New(appCfg.Logging).With("component", "worker", "job", "file_processing") + resCfg := appCfg.Resilience + fileQueueCfg := appCfg.FileQueue + fileSQSCfg := appCfg.FileSQS + if err := config.ValidateDistinctQueueURLs(appCfg.SQS, fileSQSCfg); err != nil { + return err + } + if strings.TrimSpace(fileSQSCfg.QueueURL) == "" { + return errors.New("FILE_SQS_QUEUE_URL is required for file worker") + } + db, err := mysqlrepo.Connect(appCfg.MySQL) + if err != nil { + return err + } + + fileRepo := mysqlrepo.NewFileManagerFileRepository(db) + sqsBreaker := resilience.NewExecutor("sqs", resCfg.SQS, appLogger, resilience.ClassifySQSError) + consumer, serializer, err := queueapp.NewFileProcessingConsumer(ctx, fileQueueCfg, fileSQSCfg, sqsBreaker) + if err != nil { + return err + } + idempotency := queueapp.NewIdempotencyStoreWithPrefix(db, fileQueueCfg.IdempotencyKeyPrefix) + useCaseDeps := service.FileProcessingUseCaseDependencies{ + Logger: appLogger, + FileStatusEvents: fileRepo, + GotenbergURL: appCfg.Gotenberg.URL, + } + fileStorageCfg := appCfg.FileStorage + if fileStorageCfg.Bucket != "" && fileStorageCfg.Region != "" { + storage, err := s3repo.NewFileObjectStorage(ctx, fileStorageCfg) + if err != nil { + return err + } + useCaseDeps.Storage = storage + } + useCase := service.NewFileProcessingUseCase(fileRepo, useCaseDeps) + handler := queue.NewFileProcessingJobQueueHandler(useCase, service.IsFileProcessingPermanentError) + var dispatcher *appworker.FileProcessingOutboxDispatcher + if fileQueueCfg.OutboxEnabled { + rawProducer, err := queueapp.NewQueueProducer(ctx, fileSQSCfg, sqsBreaker, "outbox_dispatcher") + if err != nil { + return err + } + dispatcher = appworker.NewFileProcessingOutboxDispatcher(fileQueueCfg, fileRepo, rawProducer, appLogger) + } + runner := appworker.NewFileProcessingRunner(fileQueueCfg, consumer, serializer, handler, idempotency, dispatcher, appLogger) + + appLogger.Info("file worker started", + "backend", "sqs", + "monitor_addr", fileQueueCfg.WorkerMonitorAddr, + "queue_url", fileSQSCfg.QueueURL, + "outbox_enabled", fileQueueCfg.OutboxEnabled, + ) + if err := runner.Start(ctx); err != nil { + return err + } + appLogger.Info("file worker stopped") + return nil +} diff --git a/cmd/worker/mail_worker.go b/cmd/worker/mail_worker.go new file mode 100644 index 0000000..817e57e --- /dev/null +++ b/cmd/worker/mail_worker.go @@ -0,0 +1,99 @@ +package main + +import ( + "context" + "errors" + "log/slog" + "os/signal" + "syscall" + + queueapp "wucher/internal/app/queueing" + appworker "wucher/internal/app/worker" + "wucher/internal/config" + "wucher/internal/shared/pkg/logger" + mysqlrepo "wucher/internal/repository/mysql" + "wucher/internal/resilience" + "wucher/internal/service" +) + +func runMailWorker(appCfg config.AppConfig) error { + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + appLogger := logger.New(appCfg.Logging).With("component", "worker", "job", "email") + resCfg := appCfg.Resilience + emailCfg := appCfg.Email + + queueCfg := appCfg.Queue + sqsCfg := appCfg.SQS + db, err := mysqlrepo.Connect(appCfg.MySQL) + if err != nil { + return err + } + authRepo := mysqlrepo.NewAuthRepository(db) + + sqsBreaker := resilience.NewExecutor("sqs", resCfg.SQS, appLogger, resilience.ClassifySQSError) + consumer, serializer, err := queueapp.NewEmailConsumer(ctx, queueCfg, sqsCfg, sqsBreaker) + if err != nil { + return err + } + idempotency := queueapp.NewIdempotencyStore(db, queueCfg) + var dispatcher *appworker.OutboxDispatcher + if queueCfg.OutboxEnabled { + rawProducer, err := queueapp.NewQueueProducer(ctx, sqsCfg, sqsBreaker, "outbox_dispatcher") + if err != nil { + return err + } + dispatcher = appworker.NewOutboxDispatcher(queueCfg, authRepo, rawProducer, appLogger) + } + + emailSender, err := buildEmailSender(ctx, emailCfg, appCfg.SES, resCfg, appLogger) + if err != nil { + return err + } + handler := service.NewEmailDeliveryHandler(emailSender, appLogger) + runner := appworker.NewRunner(queueCfg, consumer, serializer, handler, idempotency, dispatcher, appLogger) + + appLogger.Info("worker started", + "backend", "sqs", + "email_provider", emailCfg.Provider, + "monitor_addr", queueCfg.WorkerMonitorAddr, + "outbox_enabled", queueCfg.OutboxEnabled, + ) + if err := runner.Start(ctx); err != nil { + return err + } + appLogger.Info("worker stopped") + return nil +} + +func buildEmailSender( + ctx context.Context, + emailCfg config.EmailDeliveryConfig, + sesCfg config.SESConfig, + resCfg config.ResilienceConfig, + appLogger *slog.Logger, +) (service.EmailSender, error) { + switch emailCfg.Provider { + case "", "ses": + sesBreaker := resilience.NewExecutor("ses", resCfg.SES, appLogger, resilience.ClassifySESError) + sender, err := service.NewSESSender(ctx, sesCfg) + if err != nil { + return nil, err + } + sender = sender.WithExecutor(sesBreaker) + sender = sender.WithMetricsComponent("email_worker") + if !sender.Configured() { + return nil, errors.New("ses is not configured") + } + return sender, nil + case "mock": + sender := service.NewMockEmailSender(emailCfg.MockDir) + if !sender.Configured() { + return nil, errors.New("mock email sender is not configured") + } + return sender, nil + default: + return nil, errors.New("unsupported email provider: " + emailCfg.Provider) + } +} diff --git a/cmd/worker/main.go b/cmd/worker/main.go new file mode 100644 index 0000000..7f33506 --- /dev/null +++ b/cmd/worker/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "errors" + "fmt" + "log" + "os" + "strings" + + "wucher/internal/config" +) + +const ( + workerJobEnvKey = "WORKER_JOB" + workerJobEmail = "email" + workerJobFileProcessing = "file_processing" + workerJobAliasMail = "mail" + workerJobAliasEmailWorker = "email_worker" + workerJobAliasFile = "file" + workerJobAliasFileWorker = "file_worker" +) + +var ( + workerRunFn = run + workerFatalFn = log.Fatal + runMailWorkerFn func(config.AppConfig) error = runMailWorker + runFileWorkerFn func(config.AppConfig) error = runFileWorker +) + +func configFilePath() string { + if v := os.Getenv("CONFIG_FILE"); v != "" { + return v + } + basePath := os.Getenv("CONFIG_BASE_PATH") + if basePath == "" { + basePath = "/tmp" + } + filename := os.Getenv("CONFIG_FILENAME") + if filename == "" { + filename = "config.generated.json" + } + return basePath + "/" + filename +} + +func main() { + if err := workerRunFn(); err != nil { + workerFatalFn(err) + } +} + +func run() error { + appCfg, err := config.LoadFromFile(configFilePath()) + if err != nil { + return fmt.Errorf("load config: %w", err) + } + + job := normalizeWorkerJob(os.Getenv(workerJobEnvKey)) + + switch job { + case workerJobEmail: + return runMailWorkerFn(appCfg) + case workerJobFileProcessing: + return runFileWorkerFn(appCfg) + default: + return errors.New("unsupported worker job: " + job) + } +} + +func normalizeWorkerJob(raw string) string { + job := strings.ToLower(strings.TrimSpace(raw)) + + switch job { + case "", workerJobEmail, workerJobAliasMail, workerJobAliasEmailWorker: + return workerJobEmail + case workerJobFileProcessing, workerJobAliasFile, workerJobAliasFileWorker: + return workerJobFileProcessing + default: + return job + } +} diff --git a/cmd/worker/main_test.go b/cmd/worker/main_test.go new file mode 100644 index 0000000..85aaf9f --- /dev/null +++ b/cmd/worker/main_test.go @@ -0,0 +1,136 @@ +package main + +import ( + "context" + "os" + "path/filepath" + "strings" + "testing" + + "wucher/internal/config" + "wucher/internal/shared/pkg/logger" +) + +// writeTestConfig writes a minimal valid JSON config to a temp file and sets +// CONFIG_FILE so that run() can load it. +func writeTestConfig(t *testing.T) { + t.Helper() + f := filepath.Join(t.TempDir(), "config.json") + if err := os.WriteFile(f, []byte("{}"), 0o600); err != nil { + t.Fatalf("writeTestConfig: %v", err) + } + t.Setenv("CONFIG_FILE", f) +} + +func TestMain_CallsRun(t *testing.T) { + origRun := workerRunFn + t.Cleanup(func() { workerRunFn = origRun }) + + runCalled := false + workerRunFn = func() error { + runCalled = true + return nil + } + + main() + + if !runCalled { + t.Fatalf("expected worker run function to be called") + } +} + +func TestRun_DefaultsToMailWorker(t *testing.T) { + origMail := runMailWorkerFn + origFile := runFileWorkerFn + t.Cleanup(func() { + runMailWorkerFn = origMail + runFileWorkerFn = origFile + }) + + writeTestConfig(t) + t.Setenv(workerJobEnvKey, "") + mailCalled := false + fileCalled := false + runMailWorkerFn = func(config.AppConfig) error { + mailCalled = true + return nil + } + runFileWorkerFn = func(config.AppConfig) error { + fileCalled = true + return nil + } + + if err := run(); err != nil { + t.Fatalf("run: %v", err) + } + if !mailCalled { + t.Fatalf("expected mail worker to be called") + } + if fileCalled { + t.Fatalf("expected file worker not to be called") + } +} + +func TestRun_UsesFileWorkerWhenConfigured(t *testing.T) { + origMail := runMailWorkerFn + origFile := runFileWorkerFn + t.Cleanup(func() { + runMailWorkerFn = origMail + runFileWorkerFn = origFile + }) + + writeTestConfig(t) + t.Setenv(workerJobEnvKey, workerJobFileProcessing) + mailCalled := false + fileCalled := false + runMailWorkerFn = func(config.AppConfig) error { + mailCalled = true + return nil + } + runFileWorkerFn = func(config.AppConfig) error { + fileCalled = true + return nil + } + + if err := run(); err != nil { + t.Fatalf("run: %v", err) + } + if !fileCalled { + t.Fatalf("expected file worker to be called") + } + if mailCalled { + t.Fatalf("expected mail worker not to be called") + } +} + +func TestRun_ReturnsErrorOnUnsupportedWorkerJob(t *testing.T) { + writeTestConfig(t) + t.Setenv(workerJobEnvKey, "unknown_worker") + + err := run() + if err == nil || !strings.Contains(err.Error(), "unsupported worker job") { + t.Fatalf("expected unsupported worker job error, got %v", err) + } +} + +func TestBuildEmailSenderMock(t *testing.T) { + sender, err := buildEmailSender(context.Background(), config.EmailDeliveryConfig{ + Provider: "mock", + MockDir: t.TempDir(), + }, config.SESConfig{}, config.ResilienceConfig{}, logger.NewDiscard()) + if err != nil { + t.Fatalf("buildEmailSender: %v", err) + } + if sender == nil { + t.Fatalf("expected sender") + } +} + +func TestBuildEmailSenderUnsupportedProvider(t *testing.T) { + _, err := buildEmailSender(context.Background(), config.EmailDeliveryConfig{ + Provider: "unknown", + }, config.SESConfig{}, config.ResilienceConfig{}, logger.NewDiscard()) + if err == nil || !strings.Contains(err.Error(), "unsupported email provider") { + t.Fatalf("expected unsupported provider error, got %v", err) + } +} diff --git a/config/local/config.json b/config/local/config.json new file mode 100644 index 0000000..b6db2b5 --- /dev/null +++ b/config/local/config.json @@ -0,0 +1,310 @@ +{ + "env": "${APP_ENV}", + "http": { + "port": ${HTTP_PORT}, + "bodyLimit": ${HTTP_BODY_LIMIT}, + "timeouts": { + "read": "${HTTP_READ_TIMEOUT}", + "write": "${HTTP_WRITE_TIMEOUT}", + "idle": "${HTTP_IDLE_TIMEOUT}", + "request": "${HTTP_REQUEST_TIMEOUT}", + "requestOverrides": "${HTTP_REQUEST_TIMEOUT_OVERRIDES}", + "shutdown": "${HTTP_SHUTDOWN_TIMEOUT}" + }, + "cors": { + "allowOrigins": "${HTTP_CORS_ALLOW_ORIGINS}", + "allowMethods": "${HTTP_CORS_ALLOW_METHODS}", + "allowHeaders": "${HTTP_CORS_ALLOW_HEADERS}", + "exposeHeaders": "${HTTP_CORS_EXPOSE_HEADERS}", + "allowCredentials": ${HTTP_CORS_ALLOW_CREDENTIALS}, + "maxAge": ${HTTP_CORS_MAX_AGE} + }, + "rateLimit": { + "max": ${HTTP_RATE_LIMIT_MAX}, + "window": "${HTTP_RATE_LIMIT_WINDOW}", + "endpoints": "${HTTP_RATE_LIMIT_ENDPOINTS}" + }, + "security": { + "hstsMaxAge": ${HTTP_SECURITY_HSTS_MAX_AGE} + } + }, + "database": { + "mysql": { + "dsn": "${MYSQL_DSN}", + "maxOpenConns": ${MYSQL_MAX_OPEN_CONNS}, + "maxIdleConns": ${MYSQL_MAX_IDLE_CONNS}, + "connMaxLifetime": "${MYSQL_CONN_MAX_LIFETIME}", + "connMaxIdleTime": "${MYSQL_CONN_MAX_IDLE_TIME}" + } + }, + "logging": { + "level": "${LOG_LEVEL}", + "format": "${LOG_FORMAT}" + }, + "audit": { + "queueSize": ${AUDIT_LOG_QUEUE_SIZE}, + "workers": ${AUDIT_LOG_WORKERS} + }, + "email": { + "provider": "${EMAIL_PROVIDER}", + "mockDir": "${EMAIL_MOCK_DIR}" + }, + "circuitBreaker": { + "default": { + "enabled": ${CB_DEFAULT_ENABLED}, + "maxRequests": ${CB_DEFAULT_MAX_REQUESTS}, + "interval": "${CB_DEFAULT_INTERVAL}", + "bucketPeriod": "${CB_DEFAULT_BUCKET_PERIOD}", + "timeout": "${CB_DEFAULT_TIMEOUT}", + "minRequests": ${CB_DEFAULT_MIN_REQUESTS}, + "failureRatio": ${CB_DEFAULT_FAILURE_RATIO}, + "consecutiveFailures": ${CB_DEFAULT_CONSECUTIVE_FAILURES} + }, + "microsoftSSO": { + "enabled": ${CB_MICROSOFT_SSO_ENABLED}, + "maxRequests": ${CB_MICROSOFT_SSO_MAX_REQUESTS}, + "interval": "${CB_MICROSOFT_SSO_INTERVAL}", + "bucketPeriod": "${CB_MICROSOFT_SSO_BUCKET_PERIOD}", + "timeout": "${CB_MICROSOFT_SSO_TIMEOUT}", + "minRequests": ${CB_MICROSOFT_SSO_MIN_REQUESTS}, + "failureRatio": ${CB_MICROSOFT_SSO_FAILURE_RATIO}, + "consecutiveFailures": ${CB_MICROSOFT_SSO_CONSECUTIVE_FAILURES} + }, + "ses": { + "enabled": ${CB_SES_ENABLED}, + "maxRequests": ${CB_SES_MAX_REQUESTS}, + "interval": "${CB_SES_INTERVAL}", + "bucketPeriod": "${CB_SES_BUCKET_PERIOD}", + "timeout": "${CB_SES_TIMEOUT}", + "minRequests": ${CB_SES_MIN_REQUESTS}, + "failureRatio": ${CB_SES_FAILURE_RATIO}, + "consecutiveFailures": ${CB_SES_CONSECUTIVE_FAILURES} + }, + "sqs": { + "enabled": ${CB_SQS_ENABLED}, + "maxRequests": ${CB_SQS_MAX_REQUESTS}, + "interval": "${CB_SQS_INTERVAL}", + "bucketPeriod": "${CB_SQS_BUCKET_PERIOD}", + "timeout": "${CB_SQS_TIMEOUT}", + "minRequests": ${CB_SQS_MIN_REQUESTS}, + "failureRatio": ${CB_SQS_FAILURE_RATIO}, + "consecutiveFailures": ${CB_SQS_CONSECUTIVE_FAILURES} + } + }, + "auth": { + "publicBaseURL": "${APP_PUBLIC_BASE_URL}", + "passwordPepper": "${AUTH_PASSWORD_PEPPER}", + "ipEncryptionKey": "${AUTH_IP_ENCRYPTION_KEY}", + "defaultRole": "${AUTH_DEFAULT_ROLE}", + "disableRegister": ${AUTH_DISABLE_REGISTER}, + "hash": { + "time": ${AUTH_HASH_TIME}, + "memoryKB": ${AUTH_HASH_MEMORY_KB}, + "threads": ${AUTH_HASH_THREADS}, + "keyLen": ${AUTH_HASH_KEY_LEN} + }, + "email": { + "verifyTTL": "${AUTH_EMAIL_VERIFY_TTL}", + "verifyURL": "${AUTH_EMAIL_VERIFY_URL}", + "inviteTTL": "${AUTH_INVITE_TTL}" + }, + "password": { + "setURL": "${AUTH_SET_PASSWORD_URL}", + "resetTTL": "${AUTH_PASSWORD_RESET_TTL}", + "resetURL": "${AUTH_PASSWORD_RESET_URL}", + "forgotEmailCooldown": "${AUTH_FORGOT_PASSWORD_EMAIL_COOLDOWN}", + "forgotMinDuration": "${AUTH_FORGOT_PASSWORD_MIN_DURATION}", + "forgotIPRateMax": ${AUTH_FORGOT_PASSWORD_IP_RATE_MAX}, + "forgotIPRateWindow": "${AUTH_FORGOT_PASSWORD_IP_RATE_WINDOW}", + "loginIPRateMax": ${AUTH_LOGIN_IP_RATE_MAX}, + "loginIPRateWindow": "${AUTH_LOGIN_IP_RATE_WINDOW}" + }, + "securityPin": { + "resetTTL": "${AUTH_SECURITY_PIN_RESET_TTL}", + "resetURL": "${AUTH_SECURITY_PIN_RESET_URL}", + "forgotEmailCooldown": "${AUTH_FORGOT_SECURITY_PIN_EMAIL_COOLDOWN}", + "forgotMinDuration": "${AUTH_FORGOT_SECURITY_PIN_MIN_DURATION}", + "maxAttempts": ${AUTH_SECURITY_PIN_MAX_ATTEMPTS}, + "lockDuration": "${AUTH_SECURITY_PIN_LOCK_DURATION}", + "actionTokenTTL": "${AUTH_SECURITY_PIN_ACTION_TOKEN_TTL}" + }, + "jwt": { + "accessSecret": "${AUTH_JWT_ACCESS_SECRET}", + "refreshSecret": "${AUTH_JWT_REFRESH_SECRET}", + "accessTTL": "${AUTH_JWT_ACCESS_TTL}", + "refreshTTL": "${AUTH_JWT_REFRESH_TTL}", + "refreshTotpTTL": "${AUTH_JWT_REFRESH_TOTP_TTL}", + "cookie": { + "domain": "${AUTH_JWT_COOKIE_DOMAIN}", + "secure": ${AUTH_JWT_COOKIE_SECURE}, + "sameSite": "${AUTH_JWT_COOKIE_SAMESITE}", + "accessName": "${AUTH_JWT_ACCESS_COOKIE}", + "refreshName": "${AUTH_JWT_REFRESH_COOKIE}" + } + }, + "totp": { + "challengeTTL": "${AUTH_TOTP_CHALLENGE_TTL}" + }, + "sso": { + "stateTTL": "${AUTH_SSO_STATE_TTL}", + "successRedirect": "${AUTH_SSO_SUCCESS_REDIRECT}" + }, + "webauthn": { + "rpId": "${AUTH_WEBAUTHN_RP_ID}", + "rpDisplayName": "${AUTH_WEBAUTHN_RP_DISPLAY_NAME}", + "rpOrigins": "${AUTH_WEBAUTHN_RP_ORIGINS}", + "challengeTTL": "${AUTH_WEBAUTHN_CHALLENGE_TTL}" + } + }, + "totp": { + "issuer": "${TOTP_ISSUER}", + "secretKey": "${TOTP_SECRET_KEY}" + }, + "microsoftSSO": { + "tenantId": "${MS_ENTRA_TENANT_ID}", + "clientId": "${MS_ENTRA_CLIENT_ID}", + "clientSecret": "${MS_ENTRA_CLIENT_SECRET}", + "redirectUrl": "${MS_ENTRA_REDIRECT_URL}", + "authority": "${MS_ENTRA_AUTHORITY}", + "scopes": "${MS_ENTRA_SCOPES}" + }, + "saml": { + "certFile": "${SAML_CERT_FILE}", + "keyFile": "${SAML_KEY_FILE}", + "metadataFile": "${SAML_METADATA_FILE}", + "rootUrl": "${SAML_ROOT_URL}", + "frontendUrl": "${FRONTEND_URL}", + "cookieDomain": "${COOKIE_DOMAIN}", + "cookieSecure": ${COOKIE_SECURE}, + "sessionTTLHour": ${SESSION_TTL_HOUR} + }, + "queue": { + "messageSchemaVersion": "${QUEUE_MESSAGE_SCHEMA_VERSION}", + "enableLegacyPayloadFallback": ${QUEUE_ENABLE_LEGACY_PAYLOAD_FALLBACK}, + "idempotencyKeyPrefix": "${QUEUE_IDEMPOTENCY_KEY_PREFIX}", + "idempotencyTTL": "${QUEUE_IDEMPOTENCY_TTL}", + "processingTTL": "${QUEUE_PROCESSING_TTL}", + "worker": { + "concurrency": ${QUEUE_WORKER_CONCURRENCY}, + "maxBatchSize": ${QUEUE_WORKER_MAX_BATCH_SIZE}, + "processTimeout": "${QUEUE_WORKER_PROCESS_TIMEOUT}", + "shutdownTimeout": "${QUEUE_WORKER_SHUTDOWN_TIMEOUT}", + "backoffMin": "${QUEUE_WORKER_BACKOFF_MIN}", + "backoffMax": "${QUEUE_WORKER_BACKOFF_MAX}", + "permanentFailureDelay": "${QUEUE_WORKER_PERMANENT_FAILURE_DELAY}", + "monitorAddr": "${QUEUE_WORKER_MONITOR_ADDR}", + "healthErrorThreshold": ${QUEUE_WORKER_HEALTH_ERROR_THRESHOLD}, + "depthPollInterval": "${QUEUE_WORKER_DEPTH_POLL_INTERVAL}" + }, + "outbox": { + "enabled": ${QUEUE_OUTBOX_ENABLED}, + "pollInterval": "${QUEUE_OUTBOX_POLL_INTERVAL}", + "batchSize": ${QUEUE_OUTBOX_BATCH_SIZE}, + "dispatchTimeout": "${QUEUE_OUTBOX_DISPATCH_TIMEOUT}", + "lockTTL": "${QUEUE_OUTBOX_LOCK_TTL}", + "maxAttempts": ${QUEUE_OUTBOX_MAX_ATTEMPTS} + } + }, + "fileQueue": { + "messageSchemaVersion": "${FILE_QUEUE_MESSAGE_SCHEMA_VERSION}", + "idempotencyKeyPrefix": "${FILE_QUEUE_IDEMPOTENCY_KEY_PREFIX}", + "idempotencyTTL": "${FILE_QUEUE_IDEMPOTENCY_TTL}", + "processingTTL": "${FILE_QUEUE_PROCESSING_TTL}", + "worker": { + "concurrency": ${FILE_QUEUE_WORKER_CONCURRENCY}, + "maxBatchSize": ${FILE_QUEUE_WORKER_MAX_BATCH_SIZE}, + "processTimeout": "${FILE_QUEUE_WORKER_PROCESS_TIMEOUT}", + "shutdownTimeout": "${FILE_QUEUE_WORKER_SHUTDOWN_TIMEOUT}", + "backoffMin": "${FILE_QUEUE_WORKER_BACKOFF_MIN}", + "backoffMax": "${FILE_QUEUE_WORKER_BACKOFF_MAX}", + "permanentFailureDelay": "${FILE_QUEUE_WORKER_PERMANENT_FAILURE_DELAY}", + "monitorAddr": "${FILE_QUEUE_WORKER_MONITOR_ADDR}", + "healthErrorThreshold": ${FILE_QUEUE_WORKER_HEALTH_ERROR_THRESHOLD}, + "depthPollInterval": "${FILE_QUEUE_WORKER_DEPTH_POLL_INTERVAL}" + }, + "outbox": { + "enabled": ${FILE_QUEUE_OUTBOX_ENABLED}, + "pollInterval": "${FILE_QUEUE_OUTBOX_POLL_INTERVAL}", + "batchSize": ${FILE_QUEUE_OUTBOX_BATCH_SIZE}, + "dispatchTimeout": "${FILE_QUEUE_OUTBOX_DISPATCH_TIMEOUT}", + "lockTTL": "${FILE_QUEUE_OUTBOX_LOCK_TTL}", + "maxAttempts": ${FILE_QUEUE_OUTBOX_MAX_ATTEMPTS} + } + }, + "aws": { + "region": "${AWS_REGION}", + "accessKeyId": "${AWS_ACCESS_KEY_ID}", + "secretAccessKey": "${AWS_SECRET_ACCESS_KEY}", + "sqs": { + "endpoint": "${SQS_ENDPOINT}", + "allowInsecureEndpoint": ${SQS_ALLOW_INSECURE_ENDPOINT}, + "queueUrl": "${SQS_QUEUE_URL}", + "queueType": "${SQS_QUEUE_TYPE}", + "messageGroupId": "${SQS_MESSAGE_GROUP_ID}", + "maxMessages": ${SQS_MAX_MESSAGES}, + "waitTimeSeconds": ${SQS_WAIT_TIME_SECONDS}, + "visibilityTimeout": "${SQS_VISIBILITY_TIMEOUT}" + }, + "fileSqs": { + "region": "${FILE_SQS_REGION}", + "endpoint": "${FILE_SQS_ENDPOINT}", + "allowInsecureEndpoint": ${FILE_SQS_ALLOW_INSECURE_ENDPOINT}, + "queueUrl": "${FILE_SQS_QUEUE_URL}", + "queueType": "${FILE_SQS_QUEUE_TYPE}", + "messageGroupId": "${FILE_SQS_MESSAGE_GROUP_ID}", + "maxMessages": ${FILE_SQS_MAX_MESSAGES}, + "waitTimeSeconds": ${FILE_SQS_WAIT_TIME_SECONDS}, + "visibilityTimeout": "${FILE_SQS_VISIBILITY_TIMEOUT}" + }, + "ses": { + "region": "${SES_REGION}", + "from": "${SES_FROM}", + "configurationSet": "${SES_CONFIGURATION_SET}", + "sendTimeout": "${SES_SEND_TIMEOUT}", + "endpoint": "${SES_ENDPOINT}", + "allowInsecureEndpoint": ${SES_ALLOW_INSECURE_ENDPOINT} + } + }, + "file": { + "provider": "${FILE_STORAGE_PROVIDER}", + "s3": { + "bucket": "${FILE_S3_BUCKET}", + "region": "${FILE_S3_REGION}", + "endpoint": "${FILE_S3_ENDPOINT}", + "forcePathStyle": ${FILE_S3_FORCE_PATH_STYLE}, + "allowInsecureEndpoint": ${FILE_S3_ALLOW_INSECURE_ENDPOINT}, + "autoCreateBucket": ${FILE_S3_AUTO_CREATE_BUCKET}, + "objectKeyPrefix": "${FILE_OBJECT_KEY_PREFIX}", + "presignPutTTL": "${FILE_S3_PRESIGN_PUT_TTL}", + "presignGetTTL": "${FILE_S3_PRESIGN_GET_TTL}" + }, + "upload": { + "maxSize": ${FILE_MANAGER_UPLOAD_MAX_FILE_SIZE_BYTES}, + "allowedMimeTypes": "${FILE_MANAGER_UPLOAD_ALLOWED_MIME_TYPES}", + "allowedExtensions": "${FILE_MANAGER_UPLOAD_ALLOWED_EXTENSIONS}", + "allowEmptyFile": ${FILE_MANAGER_UPLOAD_ALLOW_EMPTY_FILE}, + "maxFilenameLength": ${FILE_MANAGER_UPLOAD_MAX_FILENAME_LENGTH} + }, + "cleanup": { + "enableS3Tagging": ${ENABLE_S3_TAGGING}, + "enableCleanupJob": ${ENABLE_FILE_CLEANUP_JOB}, + "tempFileTTLDays": ${TEMP_FILE_TTL_DAYS}, + "orphanGracePeriodDays": ${ORPHAN_GRACE_PERIOD_DAYS}, + "batchSize": ${FILE_CLEANUP_BATCH_SIZE}, + "dryRun": ${FILE_CLEANUP_DRY_RUN} + } + }, + "wopi": { + "tokenSecret": "${WOPI_TOKEN_SECRET}", + "proofSecret": "${WOPI_PROOF_SECRET}", + "discoveryUrl": "${WOPI_DISCOVERY_URL}", + "publicBaseUrl": "${WOPI_PUBLIC_BASE_URL}", + "editActionUrl": "${WOPI_EDIT_ACTION_URL}", + "tokenTTL": "${WOPI_TOKEN_TTL}", + "maxBodyBytes": ${WOPI_MAX_BODY_BYTES}, + "requireProof": ${WOPI_REQUIRE_PROOF} + }, + "gotenberg": { + "url": "${GOTENBERG_URL}" + } +} diff --git a/config/prod/config.json b/config/prod/config.json new file mode 100644 index 0000000..6bfb938 --- /dev/null +++ b/config/prod/config.json @@ -0,0 +1,307 @@ +{ + "env": "${APP_ENV}", + "http": { + "port": ${HTTP_PORT}, + "bodyLimit": ${HTTP_BODY_LIMIT}, + "timeouts": { + "read": "${HTTP_READ_TIMEOUT}", + "write": "${HTTP_WRITE_TIMEOUT}", + "idle": "${HTTP_IDLE_TIMEOUT}", + "request": "${HTTP_REQUEST_TIMEOUT}", + "requestOverrides": "${HTTP_REQUEST_TIMEOUT_OVERRIDES}", + "shutdown": "${HTTP_SHUTDOWN_TIMEOUT}" + }, + "cors": { + "allowOrigins": "${HTTP_CORS_ALLOW_ORIGINS}", + "allowMethods": "${HTTP_CORS_ALLOW_METHODS}", + "allowHeaders": "${HTTP_CORS_ALLOW_HEADERS}", + "exposeHeaders": "${HTTP_CORS_EXPOSE_HEADERS}", + "allowCredentials": ${HTTP_CORS_ALLOW_CREDENTIALS}, + "maxAge": ${HTTP_CORS_MAX_AGE} + }, + "rateLimit": { + "max": ${HTTP_RATE_LIMIT_MAX}, + "window": "${HTTP_RATE_LIMIT_WINDOW}", + "endpoints": "${HTTP_RATE_LIMIT_ENDPOINTS}" + }, + "security": { + "hstsMaxAge": ${HTTP_SECURITY_HSTS_MAX_AGE} + } + }, + "database": { + "mysql": { + "dsn": "${MYSQL_DSN}", + "maxOpenConns": ${MYSQL_MAX_OPEN_CONNS}, + "maxIdleConns": ${MYSQL_MAX_IDLE_CONNS}, + "connMaxLifetime": "${MYSQL_CONN_MAX_LIFETIME}", + "connMaxIdleTime": "${MYSQL_CONN_MAX_IDLE_TIME}" + } + }, + "logging": { + "level": "${LOG_LEVEL}", + "format": "${LOG_FORMAT}" + }, + "audit": { + "queueSize": ${AUDIT_LOG_QUEUE_SIZE}, + "workers": ${AUDIT_LOG_WORKERS} + }, + "email": { + "provider": "${EMAIL_PROVIDER}", + "mockDir": "${EMAIL_MOCK_DIR}" + }, + "circuitBreaker": { + "default": { + "enabled": ${CB_DEFAULT_ENABLED}, + "maxRequests": ${CB_DEFAULT_MAX_REQUESTS}, + "interval": "${CB_DEFAULT_INTERVAL}", + "bucketPeriod": "${CB_DEFAULT_BUCKET_PERIOD}", + "timeout": "${CB_DEFAULT_TIMEOUT}", + "minRequests": ${CB_DEFAULT_MIN_REQUESTS}, + "failureRatio": ${CB_DEFAULT_FAILURE_RATIO}, + "consecutiveFailures": ${CB_DEFAULT_CONSECUTIVE_FAILURES} + }, + "microsoftSSO": { + "enabled": ${CB_MICROSOFT_SSO_ENABLED}, + "maxRequests": ${CB_MICROSOFT_SSO_MAX_REQUESTS}, + "interval": "${CB_MICROSOFT_SSO_INTERVAL}", + "bucketPeriod": "${CB_MICROSOFT_SSO_BUCKET_PERIOD}", + "timeout": "${CB_MICROSOFT_SSO_TIMEOUT}", + "minRequests": ${CB_MICROSOFT_SSO_MIN_REQUESTS}, + "failureRatio": ${CB_MICROSOFT_SSO_FAILURE_RATIO}, + "consecutiveFailures": ${CB_MICROSOFT_SSO_CONSECUTIVE_FAILURES} + }, + "ses": { + "enabled": ${CB_SES_ENABLED}, + "maxRequests": ${CB_SES_MAX_REQUESTS}, + "interval": "${CB_SES_INTERVAL}", + "bucketPeriod": "${CB_SES_BUCKET_PERIOD}", + "timeout": "${CB_SES_TIMEOUT}", + "minRequests": ${CB_SES_MIN_REQUESTS}, + "failureRatio": ${CB_SES_FAILURE_RATIO}, + "consecutiveFailures": ${CB_SES_CONSECUTIVE_FAILURES} + }, + "sqs": { + "enabled": ${CB_SQS_ENABLED}, + "maxRequests": ${CB_SQS_MAX_REQUESTS}, + "interval": "${CB_SQS_INTERVAL}", + "bucketPeriod": "${CB_SQS_BUCKET_PERIOD}", + "timeout": "${CB_SQS_TIMEOUT}", + "minRequests": ${CB_SQS_MIN_REQUESTS}, + "failureRatio": ${CB_SQS_FAILURE_RATIO}, + "consecutiveFailures": ${CB_SQS_CONSECUTIVE_FAILURES} + } + }, + "auth": { + "publicBaseURL": "${APP_PUBLIC_BASE_URL}", + "passwordPepper": "${AUTH_PASSWORD_PEPPER}", + "ipEncryptionKey": "${AUTH_IP_ENCRYPTION_KEY}", + "defaultRole": "${AUTH_DEFAULT_ROLE}", + "disableRegister": ${AUTH_DISABLE_REGISTER}, + "hash": { + "time": ${AUTH_HASH_TIME}, + "memoryKB": ${AUTH_HASH_MEMORY_KB}, + "threads": ${AUTH_HASH_THREADS}, + "keyLen": ${AUTH_HASH_KEY_LEN} + }, + "email": { + "verifyTTL": "${AUTH_EMAIL_VERIFY_TTL}", + "verifyURL": "${AUTH_EMAIL_VERIFY_URL}", + "inviteTTL": "${AUTH_INVITE_TTL}" + }, + "password": { + "setURL": "${AUTH_SET_PASSWORD_URL}", + "resetTTL": "${AUTH_PASSWORD_RESET_TTL}", + "resetURL": "${AUTH_PASSWORD_RESET_URL}", + "forgotEmailCooldown": "${AUTH_FORGOT_PASSWORD_EMAIL_COOLDOWN}", + "forgotMinDuration": "${AUTH_FORGOT_PASSWORD_MIN_DURATION}", + "forgotIPRateMax": ${AUTH_FORGOT_PASSWORD_IP_RATE_MAX}, + "forgotIPRateWindow": "${AUTH_FORGOT_PASSWORD_IP_RATE_WINDOW}", + "loginIPRateMax": ${AUTH_LOGIN_IP_RATE_MAX}, + "loginIPRateWindow": "${AUTH_LOGIN_IP_RATE_WINDOW}" + }, + "securityPin": { + "resetTTL": "${AUTH_SECURITY_PIN_RESET_TTL}", + "resetURL": "${AUTH_SECURITY_PIN_RESET_URL}", + "forgotEmailCooldown": "${AUTH_FORGOT_SECURITY_PIN_EMAIL_COOLDOWN}", + "forgotMinDuration": "${AUTH_FORGOT_SECURITY_PIN_MIN_DURATION}", + "maxAttempts": ${AUTH_SECURITY_PIN_MAX_ATTEMPTS}, + "lockDuration": "${AUTH_SECURITY_PIN_LOCK_DURATION}", + "actionTokenTTL": "${AUTH_SECURITY_PIN_ACTION_TOKEN_TTL}" + }, + "jwt": { + "accessSecret": "${AUTH_JWT_ACCESS_SECRET}", + "refreshSecret": "${AUTH_JWT_REFRESH_SECRET}", + "accessTTL": "${AUTH_JWT_ACCESS_TTL}", + "refreshTTL": "${AUTH_JWT_REFRESH_TTL}", + "refreshTotpTTL": "${AUTH_JWT_REFRESH_TOTP_TTL}", + "cookie": { + "domain": "${AUTH_JWT_COOKIE_DOMAIN}", + "secure": ${AUTH_JWT_COOKIE_SECURE}, + "sameSite": "${AUTH_JWT_COOKIE_SAMESITE}", + "accessName": "${AUTH_JWT_ACCESS_COOKIE}", + "refreshName": "${AUTH_JWT_REFRESH_COOKIE}" + } + }, + "totp": { + "challengeTTL": "${AUTH_TOTP_CHALLENGE_TTL}" + }, + "sso": { + "stateTTL": "${AUTH_SSO_STATE_TTL}", + "successRedirect": "${AUTH_SSO_SUCCESS_REDIRECT}" + }, + "webauthn": { + "rpId": "${AUTH_WEBAUTHN_RP_ID}", + "rpDisplayName": "${AUTH_WEBAUTHN_RP_DISPLAY_NAME}", + "rpOrigins": "${AUTH_WEBAUTHN_RP_ORIGINS}", + "challengeTTL": "${AUTH_WEBAUTHN_CHALLENGE_TTL}" + } + }, + "totp": { + "issuer": "${TOTP_ISSUER}", + "secretKey": "${TOTP_SECRET_KEY}" + }, + "microsoftSSO": { + "tenantId": "${MS_ENTRA_TENANT_ID}", + "clientId": "${MS_ENTRA_CLIENT_ID}", + "clientSecret": "${MS_ENTRA_CLIENT_SECRET}", + "redirectUrl": "${MS_ENTRA_REDIRECT_URL}", + "authority": "${MS_ENTRA_AUTHORITY}", + "scopes": "${MS_ENTRA_SCOPES}" + }, + "saml": { + "certFile": "${SAML_CERT_FILE}", + "keyFile": "${SAML_KEY_FILE}", + "metadataFile": "${SAML_METADATA_FILE}", + "rootUrl": "${SAML_ROOT_URL}", + "frontendUrl": "${FRONTEND_URL}", + "cookieDomain": "${COOKIE_DOMAIN}", + "cookieSecure": ${COOKIE_SECURE}, + "sessionTTLHour": ${SESSION_TTL_HOUR} + }, + "queue": { + "messageSchemaVersion": "${QUEUE_MESSAGE_SCHEMA_VERSION}", + "enableLegacyPayloadFallback": ${QUEUE_ENABLE_LEGACY_PAYLOAD_FALLBACK}, + "idempotencyKeyPrefix": "${QUEUE_IDEMPOTENCY_KEY_PREFIX}", + "idempotencyTTL": "${QUEUE_IDEMPOTENCY_TTL}", + "processingTTL": "${QUEUE_PROCESSING_TTL}", + "worker": { + "concurrency": ${QUEUE_WORKER_CONCURRENCY}, + "maxBatchSize": ${QUEUE_WORKER_MAX_BATCH_SIZE}, + "processTimeout": "${QUEUE_WORKER_PROCESS_TIMEOUT}", + "shutdownTimeout": "${QUEUE_WORKER_SHUTDOWN_TIMEOUT}", + "backoffMin": "${QUEUE_WORKER_BACKOFF_MIN}", + "backoffMax": "${QUEUE_WORKER_BACKOFF_MAX}", + "permanentFailureDelay": "${QUEUE_WORKER_PERMANENT_FAILURE_DELAY}", + "monitorAddr": "${QUEUE_WORKER_MONITOR_ADDR}", + "healthErrorThreshold": ${QUEUE_WORKER_HEALTH_ERROR_THRESHOLD}, + "depthPollInterval": "${QUEUE_WORKER_DEPTH_POLL_INTERVAL}" + }, + "outbox": { + "enabled": ${QUEUE_OUTBOX_ENABLED}, + "pollInterval": "${QUEUE_OUTBOX_POLL_INTERVAL}", + "batchSize": ${QUEUE_OUTBOX_BATCH_SIZE}, + "dispatchTimeout": "${QUEUE_OUTBOX_DISPATCH_TIMEOUT}", + "lockTTL": "${QUEUE_OUTBOX_LOCK_TTL}", + "maxAttempts": ${QUEUE_OUTBOX_MAX_ATTEMPTS} + } + }, + "fileQueue": { + "messageSchemaVersion": "${FILE_QUEUE_MESSAGE_SCHEMA_VERSION}", + "idempotencyKeyPrefix": "${FILE_QUEUE_IDEMPOTENCY_KEY_PREFIX}", + "idempotencyTTL": "${FILE_QUEUE_IDEMPOTENCY_TTL}", + "processingTTL": "${FILE_QUEUE_PROCESSING_TTL}", + "worker": { + "concurrency": ${FILE_QUEUE_WORKER_CONCURRENCY}, + "maxBatchSize": ${FILE_QUEUE_WORKER_MAX_BATCH_SIZE}, + "processTimeout": "${FILE_QUEUE_WORKER_PROCESS_TIMEOUT}", + "shutdownTimeout": "${FILE_QUEUE_WORKER_SHUTDOWN_TIMEOUT}", + "backoffMin": "${FILE_QUEUE_WORKER_BACKOFF_MIN}", + "backoffMax": "${FILE_QUEUE_WORKER_BACKOFF_MAX}", + "permanentFailureDelay": "${FILE_QUEUE_WORKER_PERMANENT_FAILURE_DELAY}", + "monitorAddr": "${FILE_QUEUE_WORKER_MONITOR_ADDR}", + "healthErrorThreshold": ${FILE_QUEUE_WORKER_HEALTH_ERROR_THRESHOLD}, + "depthPollInterval": "${FILE_QUEUE_WORKER_DEPTH_POLL_INTERVAL}" + }, + "outbox": { + "enabled": ${FILE_QUEUE_OUTBOX_ENABLED}, + "pollInterval": "${FILE_QUEUE_OUTBOX_POLL_INTERVAL}", + "batchSize": ${FILE_QUEUE_OUTBOX_BATCH_SIZE}, + "dispatchTimeout": "${FILE_QUEUE_OUTBOX_DISPATCH_TIMEOUT}", + "lockTTL": "${FILE_QUEUE_OUTBOX_LOCK_TTL}", + "maxAttempts": ${FILE_QUEUE_OUTBOX_MAX_ATTEMPTS} + } + }, + "aws": { + "region": "${AWS_REGION}", + "accessKeyId": "${AWS_ACCESS_KEY_ID}", + "secretAccessKey": "${AWS_SECRET_ACCESS_KEY}", + "sqs": { + "endpoint": "${SQS_ENDPOINT}", + "allowInsecureEndpoint": ${SQS_ALLOW_INSECURE_ENDPOINT}, + "queueUrl": "${SQS_QUEUE_URL}", + "queueType": "${SQS_QUEUE_TYPE}", + "messageGroupId": "${SQS_MESSAGE_GROUP_ID}", + "maxMessages": ${SQS_MAX_MESSAGES}, + "waitTimeSeconds": ${SQS_WAIT_TIME_SECONDS}, + "visibilityTimeout": "${SQS_VISIBILITY_TIMEOUT}" + }, + "fileSqs": { + "region": "${FILE_SQS_REGION}", + "endpoint": "${FILE_SQS_ENDPOINT}", + "allowInsecureEndpoint": ${FILE_SQS_ALLOW_INSECURE_ENDPOINT}, + "queueUrl": "${FILE_SQS_QUEUE_URL}", + "queueType": "${FILE_SQS_QUEUE_TYPE}", + "messageGroupId": "${FILE_SQS_MESSAGE_GROUP_ID}", + "maxMessages": ${FILE_SQS_MAX_MESSAGES}, + "waitTimeSeconds": ${FILE_SQS_WAIT_TIME_SECONDS}, + "visibilityTimeout": "${FILE_SQS_VISIBILITY_TIMEOUT}" + }, + "ses": { + "region": "${SES_REGION}", + "from": "${SES_FROM}", + "configurationSet": "${SES_CONFIGURATION_SET}", + "sendTimeout": "${SES_SEND_TIMEOUT}", + "endpoint": "${SES_ENDPOINT}", + "allowInsecureEndpoint": ${SES_ALLOW_INSECURE_ENDPOINT} + } + }, + "file": { + "provider": "${FILE_STORAGE_PROVIDER}", + "s3": { + "bucket": "${FILE_S3_BUCKET}", + "region": "${FILE_S3_REGION}", + "endpoint": "${FILE_S3_ENDPOINT}", + "forcePathStyle": ${FILE_S3_FORCE_PATH_STYLE}, + "allowInsecureEndpoint": ${FILE_S3_ALLOW_INSECURE_ENDPOINT}, + "autoCreateBucket": ${FILE_S3_AUTO_CREATE_BUCKET}, + "objectKeyPrefix": "${FILE_OBJECT_KEY_PREFIX}", + "presignPutTTL": "${FILE_S3_PRESIGN_PUT_TTL}", + "presignGetTTL": "${FILE_S3_PRESIGN_GET_TTL}" + }, + "upload": { + "maxSize": ${FILE_MANAGER_UPLOAD_MAX_FILE_SIZE_BYTES}, + "allowedMimeTypes": "${FILE_MANAGER_UPLOAD_ALLOWED_MIME_TYPES}", + "allowedExtensions": "${FILE_MANAGER_UPLOAD_ALLOWED_EXTENSIONS}", + "allowEmptyFile": ${FILE_MANAGER_UPLOAD_ALLOW_EMPTY_FILE}, + "maxFilenameLength": ${FILE_MANAGER_UPLOAD_MAX_FILENAME_LENGTH} + }, + "cleanup": { + "enableS3Tagging": ${ENABLE_S3_TAGGING}, + "enableCleanupJob": ${ENABLE_FILE_CLEANUP_JOB}, + "tempFileTTLDays": ${TEMP_FILE_TTL_DAYS}, + "orphanGracePeriodDays": ${ORPHAN_GRACE_PERIOD_DAYS}, + "batchSize": ${FILE_CLEANUP_BATCH_SIZE}, + "dryRun": ${FILE_CLEANUP_DRY_RUN} + } + }, + "wopi": { + "tokenSecret": "${WOPI_TOKEN_SECRET}", + "proofSecret": "${WOPI_PROOF_SECRET}", + "discoveryUrl": "${WOPI_DISCOVERY_URL}", + "publicBaseUrl": "${WOPI_PUBLIC_BASE_URL}", + "editActionUrl": "${WOPI_EDIT_ACTION_URL}", + "tokenTTL": "${WOPI_TOKEN_TTL}", + "maxBodyBytes": ${WOPI_MAX_BODY_BYTES}, + "requireProof": ${WOPI_REQUIRE_PROOF} + } +} diff --git a/config/staging/config.json b/config/staging/config.json new file mode 100644 index 0000000..6bfb938 --- /dev/null +++ b/config/staging/config.json @@ -0,0 +1,307 @@ +{ + "env": "${APP_ENV}", + "http": { + "port": ${HTTP_PORT}, + "bodyLimit": ${HTTP_BODY_LIMIT}, + "timeouts": { + "read": "${HTTP_READ_TIMEOUT}", + "write": "${HTTP_WRITE_TIMEOUT}", + "idle": "${HTTP_IDLE_TIMEOUT}", + "request": "${HTTP_REQUEST_TIMEOUT}", + "requestOverrides": "${HTTP_REQUEST_TIMEOUT_OVERRIDES}", + "shutdown": "${HTTP_SHUTDOWN_TIMEOUT}" + }, + "cors": { + "allowOrigins": "${HTTP_CORS_ALLOW_ORIGINS}", + "allowMethods": "${HTTP_CORS_ALLOW_METHODS}", + "allowHeaders": "${HTTP_CORS_ALLOW_HEADERS}", + "exposeHeaders": "${HTTP_CORS_EXPOSE_HEADERS}", + "allowCredentials": ${HTTP_CORS_ALLOW_CREDENTIALS}, + "maxAge": ${HTTP_CORS_MAX_AGE} + }, + "rateLimit": { + "max": ${HTTP_RATE_LIMIT_MAX}, + "window": "${HTTP_RATE_LIMIT_WINDOW}", + "endpoints": "${HTTP_RATE_LIMIT_ENDPOINTS}" + }, + "security": { + "hstsMaxAge": ${HTTP_SECURITY_HSTS_MAX_AGE} + } + }, + "database": { + "mysql": { + "dsn": "${MYSQL_DSN}", + "maxOpenConns": ${MYSQL_MAX_OPEN_CONNS}, + "maxIdleConns": ${MYSQL_MAX_IDLE_CONNS}, + "connMaxLifetime": "${MYSQL_CONN_MAX_LIFETIME}", + "connMaxIdleTime": "${MYSQL_CONN_MAX_IDLE_TIME}" + } + }, + "logging": { + "level": "${LOG_LEVEL}", + "format": "${LOG_FORMAT}" + }, + "audit": { + "queueSize": ${AUDIT_LOG_QUEUE_SIZE}, + "workers": ${AUDIT_LOG_WORKERS} + }, + "email": { + "provider": "${EMAIL_PROVIDER}", + "mockDir": "${EMAIL_MOCK_DIR}" + }, + "circuitBreaker": { + "default": { + "enabled": ${CB_DEFAULT_ENABLED}, + "maxRequests": ${CB_DEFAULT_MAX_REQUESTS}, + "interval": "${CB_DEFAULT_INTERVAL}", + "bucketPeriod": "${CB_DEFAULT_BUCKET_PERIOD}", + "timeout": "${CB_DEFAULT_TIMEOUT}", + "minRequests": ${CB_DEFAULT_MIN_REQUESTS}, + "failureRatio": ${CB_DEFAULT_FAILURE_RATIO}, + "consecutiveFailures": ${CB_DEFAULT_CONSECUTIVE_FAILURES} + }, + "microsoftSSO": { + "enabled": ${CB_MICROSOFT_SSO_ENABLED}, + "maxRequests": ${CB_MICROSOFT_SSO_MAX_REQUESTS}, + "interval": "${CB_MICROSOFT_SSO_INTERVAL}", + "bucketPeriod": "${CB_MICROSOFT_SSO_BUCKET_PERIOD}", + "timeout": "${CB_MICROSOFT_SSO_TIMEOUT}", + "minRequests": ${CB_MICROSOFT_SSO_MIN_REQUESTS}, + "failureRatio": ${CB_MICROSOFT_SSO_FAILURE_RATIO}, + "consecutiveFailures": ${CB_MICROSOFT_SSO_CONSECUTIVE_FAILURES} + }, + "ses": { + "enabled": ${CB_SES_ENABLED}, + "maxRequests": ${CB_SES_MAX_REQUESTS}, + "interval": "${CB_SES_INTERVAL}", + "bucketPeriod": "${CB_SES_BUCKET_PERIOD}", + "timeout": "${CB_SES_TIMEOUT}", + "minRequests": ${CB_SES_MIN_REQUESTS}, + "failureRatio": ${CB_SES_FAILURE_RATIO}, + "consecutiveFailures": ${CB_SES_CONSECUTIVE_FAILURES} + }, + "sqs": { + "enabled": ${CB_SQS_ENABLED}, + "maxRequests": ${CB_SQS_MAX_REQUESTS}, + "interval": "${CB_SQS_INTERVAL}", + "bucketPeriod": "${CB_SQS_BUCKET_PERIOD}", + "timeout": "${CB_SQS_TIMEOUT}", + "minRequests": ${CB_SQS_MIN_REQUESTS}, + "failureRatio": ${CB_SQS_FAILURE_RATIO}, + "consecutiveFailures": ${CB_SQS_CONSECUTIVE_FAILURES} + } + }, + "auth": { + "publicBaseURL": "${APP_PUBLIC_BASE_URL}", + "passwordPepper": "${AUTH_PASSWORD_PEPPER}", + "ipEncryptionKey": "${AUTH_IP_ENCRYPTION_KEY}", + "defaultRole": "${AUTH_DEFAULT_ROLE}", + "disableRegister": ${AUTH_DISABLE_REGISTER}, + "hash": { + "time": ${AUTH_HASH_TIME}, + "memoryKB": ${AUTH_HASH_MEMORY_KB}, + "threads": ${AUTH_HASH_THREADS}, + "keyLen": ${AUTH_HASH_KEY_LEN} + }, + "email": { + "verifyTTL": "${AUTH_EMAIL_VERIFY_TTL}", + "verifyURL": "${AUTH_EMAIL_VERIFY_URL}", + "inviteTTL": "${AUTH_INVITE_TTL}" + }, + "password": { + "setURL": "${AUTH_SET_PASSWORD_URL}", + "resetTTL": "${AUTH_PASSWORD_RESET_TTL}", + "resetURL": "${AUTH_PASSWORD_RESET_URL}", + "forgotEmailCooldown": "${AUTH_FORGOT_PASSWORD_EMAIL_COOLDOWN}", + "forgotMinDuration": "${AUTH_FORGOT_PASSWORD_MIN_DURATION}", + "forgotIPRateMax": ${AUTH_FORGOT_PASSWORD_IP_RATE_MAX}, + "forgotIPRateWindow": "${AUTH_FORGOT_PASSWORD_IP_RATE_WINDOW}", + "loginIPRateMax": ${AUTH_LOGIN_IP_RATE_MAX}, + "loginIPRateWindow": "${AUTH_LOGIN_IP_RATE_WINDOW}" + }, + "securityPin": { + "resetTTL": "${AUTH_SECURITY_PIN_RESET_TTL}", + "resetURL": "${AUTH_SECURITY_PIN_RESET_URL}", + "forgotEmailCooldown": "${AUTH_FORGOT_SECURITY_PIN_EMAIL_COOLDOWN}", + "forgotMinDuration": "${AUTH_FORGOT_SECURITY_PIN_MIN_DURATION}", + "maxAttempts": ${AUTH_SECURITY_PIN_MAX_ATTEMPTS}, + "lockDuration": "${AUTH_SECURITY_PIN_LOCK_DURATION}", + "actionTokenTTL": "${AUTH_SECURITY_PIN_ACTION_TOKEN_TTL}" + }, + "jwt": { + "accessSecret": "${AUTH_JWT_ACCESS_SECRET}", + "refreshSecret": "${AUTH_JWT_REFRESH_SECRET}", + "accessTTL": "${AUTH_JWT_ACCESS_TTL}", + "refreshTTL": "${AUTH_JWT_REFRESH_TTL}", + "refreshTotpTTL": "${AUTH_JWT_REFRESH_TOTP_TTL}", + "cookie": { + "domain": "${AUTH_JWT_COOKIE_DOMAIN}", + "secure": ${AUTH_JWT_COOKIE_SECURE}, + "sameSite": "${AUTH_JWT_COOKIE_SAMESITE}", + "accessName": "${AUTH_JWT_ACCESS_COOKIE}", + "refreshName": "${AUTH_JWT_REFRESH_COOKIE}" + } + }, + "totp": { + "challengeTTL": "${AUTH_TOTP_CHALLENGE_TTL}" + }, + "sso": { + "stateTTL": "${AUTH_SSO_STATE_TTL}", + "successRedirect": "${AUTH_SSO_SUCCESS_REDIRECT}" + }, + "webauthn": { + "rpId": "${AUTH_WEBAUTHN_RP_ID}", + "rpDisplayName": "${AUTH_WEBAUTHN_RP_DISPLAY_NAME}", + "rpOrigins": "${AUTH_WEBAUTHN_RP_ORIGINS}", + "challengeTTL": "${AUTH_WEBAUTHN_CHALLENGE_TTL}" + } + }, + "totp": { + "issuer": "${TOTP_ISSUER}", + "secretKey": "${TOTP_SECRET_KEY}" + }, + "microsoftSSO": { + "tenantId": "${MS_ENTRA_TENANT_ID}", + "clientId": "${MS_ENTRA_CLIENT_ID}", + "clientSecret": "${MS_ENTRA_CLIENT_SECRET}", + "redirectUrl": "${MS_ENTRA_REDIRECT_URL}", + "authority": "${MS_ENTRA_AUTHORITY}", + "scopes": "${MS_ENTRA_SCOPES}" + }, + "saml": { + "certFile": "${SAML_CERT_FILE}", + "keyFile": "${SAML_KEY_FILE}", + "metadataFile": "${SAML_METADATA_FILE}", + "rootUrl": "${SAML_ROOT_URL}", + "frontendUrl": "${FRONTEND_URL}", + "cookieDomain": "${COOKIE_DOMAIN}", + "cookieSecure": ${COOKIE_SECURE}, + "sessionTTLHour": ${SESSION_TTL_HOUR} + }, + "queue": { + "messageSchemaVersion": "${QUEUE_MESSAGE_SCHEMA_VERSION}", + "enableLegacyPayloadFallback": ${QUEUE_ENABLE_LEGACY_PAYLOAD_FALLBACK}, + "idempotencyKeyPrefix": "${QUEUE_IDEMPOTENCY_KEY_PREFIX}", + "idempotencyTTL": "${QUEUE_IDEMPOTENCY_TTL}", + "processingTTL": "${QUEUE_PROCESSING_TTL}", + "worker": { + "concurrency": ${QUEUE_WORKER_CONCURRENCY}, + "maxBatchSize": ${QUEUE_WORKER_MAX_BATCH_SIZE}, + "processTimeout": "${QUEUE_WORKER_PROCESS_TIMEOUT}", + "shutdownTimeout": "${QUEUE_WORKER_SHUTDOWN_TIMEOUT}", + "backoffMin": "${QUEUE_WORKER_BACKOFF_MIN}", + "backoffMax": "${QUEUE_WORKER_BACKOFF_MAX}", + "permanentFailureDelay": "${QUEUE_WORKER_PERMANENT_FAILURE_DELAY}", + "monitorAddr": "${QUEUE_WORKER_MONITOR_ADDR}", + "healthErrorThreshold": ${QUEUE_WORKER_HEALTH_ERROR_THRESHOLD}, + "depthPollInterval": "${QUEUE_WORKER_DEPTH_POLL_INTERVAL}" + }, + "outbox": { + "enabled": ${QUEUE_OUTBOX_ENABLED}, + "pollInterval": "${QUEUE_OUTBOX_POLL_INTERVAL}", + "batchSize": ${QUEUE_OUTBOX_BATCH_SIZE}, + "dispatchTimeout": "${QUEUE_OUTBOX_DISPATCH_TIMEOUT}", + "lockTTL": "${QUEUE_OUTBOX_LOCK_TTL}", + "maxAttempts": ${QUEUE_OUTBOX_MAX_ATTEMPTS} + } + }, + "fileQueue": { + "messageSchemaVersion": "${FILE_QUEUE_MESSAGE_SCHEMA_VERSION}", + "idempotencyKeyPrefix": "${FILE_QUEUE_IDEMPOTENCY_KEY_PREFIX}", + "idempotencyTTL": "${FILE_QUEUE_IDEMPOTENCY_TTL}", + "processingTTL": "${FILE_QUEUE_PROCESSING_TTL}", + "worker": { + "concurrency": ${FILE_QUEUE_WORKER_CONCURRENCY}, + "maxBatchSize": ${FILE_QUEUE_WORKER_MAX_BATCH_SIZE}, + "processTimeout": "${FILE_QUEUE_WORKER_PROCESS_TIMEOUT}", + "shutdownTimeout": "${FILE_QUEUE_WORKER_SHUTDOWN_TIMEOUT}", + "backoffMin": "${FILE_QUEUE_WORKER_BACKOFF_MIN}", + "backoffMax": "${FILE_QUEUE_WORKER_BACKOFF_MAX}", + "permanentFailureDelay": "${FILE_QUEUE_WORKER_PERMANENT_FAILURE_DELAY}", + "monitorAddr": "${FILE_QUEUE_WORKER_MONITOR_ADDR}", + "healthErrorThreshold": ${FILE_QUEUE_WORKER_HEALTH_ERROR_THRESHOLD}, + "depthPollInterval": "${FILE_QUEUE_WORKER_DEPTH_POLL_INTERVAL}" + }, + "outbox": { + "enabled": ${FILE_QUEUE_OUTBOX_ENABLED}, + "pollInterval": "${FILE_QUEUE_OUTBOX_POLL_INTERVAL}", + "batchSize": ${FILE_QUEUE_OUTBOX_BATCH_SIZE}, + "dispatchTimeout": "${FILE_QUEUE_OUTBOX_DISPATCH_TIMEOUT}", + "lockTTL": "${FILE_QUEUE_OUTBOX_LOCK_TTL}", + "maxAttempts": ${FILE_QUEUE_OUTBOX_MAX_ATTEMPTS} + } + }, + "aws": { + "region": "${AWS_REGION}", + "accessKeyId": "${AWS_ACCESS_KEY_ID}", + "secretAccessKey": "${AWS_SECRET_ACCESS_KEY}", + "sqs": { + "endpoint": "${SQS_ENDPOINT}", + "allowInsecureEndpoint": ${SQS_ALLOW_INSECURE_ENDPOINT}, + "queueUrl": "${SQS_QUEUE_URL}", + "queueType": "${SQS_QUEUE_TYPE}", + "messageGroupId": "${SQS_MESSAGE_GROUP_ID}", + "maxMessages": ${SQS_MAX_MESSAGES}, + "waitTimeSeconds": ${SQS_WAIT_TIME_SECONDS}, + "visibilityTimeout": "${SQS_VISIBILITY_TIMEOUT}" + }, + "fileSqs": { + "region": "${FILE_SQS_REGION}", + "endpoint": "${FILE_SQS_ENDPOINT}", + "allowInsecureEndpoint": ${FILE_SQS_ALLOW_INSECURE_ENDPOINT}, + "queueUrl": "${FILE_SQS_QUEUE_URL}", + "queueType": "${FILE_SQS_QUEUE_TYPE}", + "messageGroupId": "${FILE_SQS_MESSAGE_GROUP_ID}", + "maxMessages": ${FILE_SQS_MAX_MESSAGES}, + "waitTimeSeconds": ${FILE_SQS_WAIT_TIME_SECONDS}, + "visibilityTimeout": "${FILE_SQS_VISIBILITY_TIMEOUT}" + }, + "ses": { + "region": "${SES_REGION}", + "from": "${SES_FROM}", + "configurationSet": "${SES_CONFIGURATION_SET}", + "sendTimeout": "${SES_SEND_TIMEOUT}", + "endpoint": "${SES_ENDPOINT}", + "allowInsecureEndpoint": ${SES_ALLOW_INSECURE_ENDPOINT} + } + }, + "file": { + "provider": "${FILE_STORAGE_PROVIDER}", + "s3": { + "bucket": "${FILE_S3_BUCKET}", + "region": "${FILE_S3_REGION}", + "endpoint": "${FILE_S3_ENDPOINT}", + "forcePathStyle": ${FILE_S3_FORCE_PATH_STYLE}, + "allowInsecureEndpoint": ${FILE_S3_ALLOW_INSECURE_ENDPOINT}, + "autoCreateBucket": ${FILE_S3_AUTO_CREATE_BUCKET}, + "objectKeyPrefix": "${FILE_OBJECT_KEY_PREFIX}", + "presignPutTTL": "${FILE_S3_PRESIGN_PUT_TTL}", + "presignGetTTL": "${FILE_S3_PRESIGN_GET_TTL}" + }, + "upload": { + "maxSize": ${FILE_MANAGER_UPLOAD_MAX_FILE_SIZE_BYTES}, + "allowedMimeTypes": "${FILE_MANAGER_UPLOAD_ALLOWED_MIME_TYPES}", + "allowedExtensions": "${FILE_MANAGER_UPLOAD_ALLOWED_EXTENSIONS}", + "allowEmptyFile": ${FILE_MANAGER_UPLOAD_ALLOW_EMPTY_FILE}, + "maxFilenameLength": ${FILE_MANAGER_UPLOAD_MAX_FILENAME_LENGTH} + }, + "cleanup": { + "enableS3Tagging": ${ENABLE_S3_TAGGING}, + "enableCleanupJob": ${ENABLE_FILE_CLEANUP_JOB}, + "tempFileTTLDays": ${TEMP_FILE_TTL_DAYS}, + "orphanGracePeriodDays": ${ORPHAN_GRACE_PERIOD_DAYS}, + "batchSize": ${FILE_CLEANUP_BATCH_SIZE}, + "dryRun": ${FILE_CLEANUP_DRY_RUN} + } + }, + "wopi": { + "tokenSecret": "${WOPI_TOKEN_SECRET}", + "proofSecret": "${WOPI_PROOF_SECRET}", + "discoveryUrl": "${WOPI_DISCOVERY_URL}", + "publicBaseUrl": "${WOPI_PUBLIC_BASE_URL}", + "editActionUrl": "${WOPI_EDIT_ACTION_URL}", + "tokenTTL": "${WOPI_TOKEN_TTL}", + "maxBodyBytes": ${WOPI_MAX_BODY_BYTES}, + "requireProof": ${WOPI_REQUIRE_PROOF} + } +} diff --git a/configs/.gitignore b/configs/.gitignore new file mode 100644 index 0000000..149d33c --- /dev/null +++ b/configs/.gitignore @@ -0,0 +1,2 @@ +saml.key +metadata.xml diff --git a/configs/saml.crt b/configs/saml.crt new file mode 100644 index 0000000..c01e614 --- /dev/null +++ b/configs/saml.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDJTCCAg2gAwIBAgIUIC8zX/OqMbovxzuzGOHr7UJVucQwDQYJKoZIhvcNAQEL +BQAwIjEgMB4GA1UEAwwXYXBpLXdmbS1kZXYubXliaXQuY28uaWQwHhcNMjYwNTI1 +MDMxMjE5WhcNMzYwNTIyMDMxMjE5WjAiMSAwHgYDVQQDDBdhcGktd2ZtLWRldi5t +eWJpdC5jby5pZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMX3z2aC +OO12ZwuNxxLf35m7rzIGB6J5O8EIgqihmg4ciy3BIkiDZAazAXaXR6ZDzLp63XMT +9a6zEA3+U+LIovxcpgK2znrfxxFI0QehpTxzQj+GIkClyy5srewYHIWrQu+ShaXn +btsjVwjELAGLzD8fLlIbmvrqr5zTCzq9Ep6ZZWCA8V8HAEXlR8lmC7vcu9bCCt5J +7zTE5JODnxTFawJpcbpBgudL/0x44ed3aglLQsfdk7mB4FiUOXup8aOefZHRzayl +n8jNSGH98kzw21vYV8bJUuwp9wNDqVNpM7LKIkdC4m+VgoTP/CM+GfRAnfpLhoP0 +oaxV6wUqmlxceQMCAwEAAaNTMFEwHQYDVR0OBBYEFHt5Aeik4E5BC6j0wc2IcfAa +FlSpMB8GA1UdIwQYMBaAFHt5Aeik4E5BC6j0wc2IcfAaFlSpMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHbt3T5iti9uguOqekecWCzti3bE41W/ +pfgT7Ub2XXU3m217tfl4EDEHf/FagNn3KtBufKsoV/57eFWb9wvCE4KAltUi0Qzn +xQtCU/80TgNErnPhPR72LB4HxWFOA+k7WobY0+PYiedRJSf8vsrho/UgkvePPtp4 +3jfmN0By9/d+KqHNilPDx1AbJEboQckp/xIEfgAoCfsXSNYeWUuQlKu9eqXaz8Om +lkUVcHfR5GBxSzqMvbbMe2EYNpB2Ap+ih/KzoBLEmbkUSeaSnUbhYYsMIHFl3usp +7Z3sEBWCfeYZeLlvsNNMse7zkBB+TyOtPDlcPXhZAY9225S2q/sd/aY= +-----END CERTIFICATE----- diff --git a/coverage.html b/coverage.html new file mode 100644 index 0000000..185acc0 --- /dev/null +++ b/coverage.html @@ -0,0 +1,5165 @@ + + + + + + api: Go Coverage Report + + + +
+ +
+ not tracked + + not covered + covered + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..edcc3bb --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,114 @@ +services: + api: + image: mybitlab/wfm:be-dev + env_file: + - .env + environment: + HTTP_PORT: "8080" + APP_ENV: development + CONFIG_BASE_PATH: /opt/flight-manager/development + volumes: + - /opt/flight-manager/development/config.generated.json:/opt/flight-manager/development/config.generated.json:ro + - ./configs:/app/configs:ro + ports: + - 8086:8080 + restart: unless-stopped + networks: + - wucher + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:8080/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" + worker: + image: mybitlab/wfm:be-dev-worker + env_file: + - .env + environment: + QUEUE_WORKER_MONITOR_ADDR: 0.0.0.0:9090 + WORKER_JOB: email + APP_ENV: development + CONFIG_BASE_PATH: /opt/flight-manager/development + volumes: + - /opt/flight-manager/development/config.generated.json:/opt/flight-manager/development/config.generated.json:ro + expose: + - 9090 + # ports: + # - 9092:9090 + restart: unless-stopped + networks: + - wucher + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:9090/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" + + file-worker: + image: mybitlab/wfm:be-dev-file-worker + env_file: + - .env + environment: + QUEUE_WORKER_MONITOR_ADDR: 0.0.0.0:9091 + WORKER_JOB: file_processing + APP_ENV: development + CONFIG_BASE_PATH: /opt/flight-manager/development + volumes: + - /opt/flight-manager/development/config.generated.json:/opt/flight-manager/development/config.generated.json:ro + expose: + - 9091 + # ports: + # - 9093:9091 + restart: unless-stopped + networks: + - wucher + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:9091/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" +networks: + wucher: + name: wucher + driver: bridge + db_net: + external: true diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml new file mode 100644 index 0000000..781ff11 --- /dev/null +++ b/docker-compose.staging.yml @@ -0,0 +1,110 @@ +services: + api: + image: mybitlab/wfm:be-staging + env_file: + - .env + environment: + HTTP_PORT: "8080" + APP_ENV: staging + CONFIG_BASE_PATH: /opt/flight-manager/staging + volumes: + - /opt/flight-manager/staging/config.generated.json:/opt/flight-manager/staging/config.generated.json:ro + - ./configs:/app/configs:ro + ports: + - 8087:8080 + restart: unless-stopped + networks: + - fm-staging + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:8080/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" + worker: + image: mybitlab/wfm:be-worker-staging + env_file: + - .env + environment: + QUEUE_WORKER_MONITOR_ADDR: 0.0.0.0:9090 + WORKER_JOB: email + APP_ENV: staging + CONFIG_BASE_PATH: /opt/flight-manager/staging + volumes: + - /opt/flight-manager/staging/config.generated.json:/opt/flight-manager/staging/config.generated.json:ro + expose: + - 9090 + restart: unless-stopped + networks: + - fm-staging + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:9090/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" + + file-worker: + image: mybitlab/wfm:be-staging-file-worker + env_file: + - .env + environment: + QUEUE_WORKER_MONITOR_ADDR: 0.0.0.0:9091 + WORKER_JOB: file_processing + APP_ENV: staging + CONFIG_BASE_PATH: /opt/flight-manager/staging + volumes: + - /opt/flight-manager/staging/config.generated.json:/opt/flight-manager/staging/config.generated.json:ro + expose: + - 9091 + restart: unless-stopped + networks: + - fm-staging + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:9091/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" +networks: + fm-staging: + external: true + db_net: + external: true + diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..1c41e0f --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,110 @@ +services: + api: + # Reuses the staging image — test differs only by its .env + DB (managed at deploy) + image: mybitlab/wfm:be-staging + env_file: + - .env + environment: + HTTP_PORT: "8080" + APP_ENV: test + CONFIG_BASE_PATH: /opt/flight-manager/test + volumes: + - /opt/flight-manager/test/config.generated.json:/opt/flight-manager/test/config.generated.json:ro + - ./configs:/app/configs:ro + ports: + - 8092:8080 + restart: unless-stopped + networks: + - fm-test + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:8080/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" + worker: + image: mybitlab/wfm:be-worker-staging + env_file: + - .env + environment: + QUEUE_WORKER_MONITOR_ADDR: 0.0.0.0:9090 + WORKER_JOB: email + APP_ENV: test + CONFIG_BASE_PATH: /opt/flight-manager/test + volumes: + - /opt/flight-manager/test/config.generated.json:/opt/flight-manager/test/config.generated.json:ro + expose: + - 9090 + restart: unless-stopped + networks: + - fm-test + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:9090/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" + + file-worker: + image: mybitlab/wfm:be-staging-file-worker + env_file: + - .env + environment: + QUEUE_WORKER_MONITOR_ADDR: 0.0.0.0:9091 + WORKER_JOB: file_processing + APP_ENV: test + CONFIG_BASE_PATH: /opt/flight-manager/test + volumes: + - /opt/flight-manager/test/config.generated.json:/opt/flight-manager/test/config.generated.json:ro + expose: + - 9091 + restart: unless-stopped + networks: + - fm-test + - db_net + extra_hosts: + - host.docker.internal:host-gateway + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: + - CMD + - /app/healthcheck + - http://127.0.0.1:9091/health + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + labels: + - "logging=enabled" +networks: + fm-test: + external: true + db_net: + external: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b139dd1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,86 @@ +# This stack runs API, email worker, and file worker containers. +# Ensure envs in .env point to endpoints reachable from inside containers. +# In particular, MYSQL_DSN/SQS/SES values using 127.0.0.1 will not work unless they are +# changed to a host or service name that containers can reach. +services: + api: + build: + context: . + dockerfile: Dockerfile + target: api + image: localhost/wucher-api:dev + env_file: + - .env + environment: + HTTP_PORT: "8080" + ports: + - "8080:8080" + restart: unless-stopped + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: ["CMD", "/app/healthcheck", "http://127.0.0.1:8080/health"] + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + + worker: + build: + context: . + dockerfile: Dockerfile + target: worker + image: localhost/wucher-worker:dev + env_file: + - .env + environment: + # Bind to all interfaces so the monitor endpoint can be reached from the host. + QUEUE_WORKER_MONITOR_ADDR: "0.0.0.0:9090" + WORKER_JOB: "email" + ports: + - "9090:9090" + restart: unless-stopped + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: ["CMD", "/app/healthcheck", "http://127.0.0.1:9090/health"] + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 + + gotenberg: + image: gotenberg/gotenberg:8 + ports: + - "3000:3000" + restart: unless-stopped + + file-worker: + build: + context: . + dockerfile: Dockerfile + target: file-worker + image: localhost/wucher-file-worker:dev + env_file: + - .env + environment: + # Bind to all interfaces so the monitor endpoint can be reached from the host. + QUEUE_WORKER_MONITOR_ADDR: "0.0.0.0:9091" + WORKER_JOB: "file_processing" + ports: + - "9091:9091" + restart: unless-stopped + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + healthcheck: + test: ["CMD", "/app/healthcheck", "http://127.0.0.1:9091/health"] + interval: 30s + timeout: 3s + start_period: 20s + retries: 3 diff --git a/docs/API_GUIDE.md b/docs/API_GUIDE.md new file mode 100644 index 0000000..709747a --- /dev/null +++ b/docs/API_GUIDE.md @@ -0,0 +1,423 @@ +# API Guide STEP 1 & STEP 2 + +Dokumentasi ringkas untuk endpoint yang paling sering dipakai di backend ini. + +## Helicopter APIs + +### 1. Create helicopter +- `POST /api/v1/helicopters/create` +- Fungsi: membuat data helicopter baru. +- Cara pakai: kirim payload create helicopter sesuai schema di Swagger. + +### 2. Update helicopter +- `PATCH /api/v1/helicopters/update/{id}` +- Fungsi: update sebagian field helicopter berdasarkan `id`. +- Cara pakai: kirim field yang ingin diubah saja. + +### 3. Delete helicopter +- `DELETE /api/v1/helicopters/delete/{id}` +- Fungsi: soft delete helicopter. +- Cara pakai: kirim `id` helicopter di path. + +### 4. Generate next report number +- `POST /api/v1/helicopters/{id}/reports/next-number` +- Fungsi: generate nomor report berikutnya dengan format `-NNNN`. +- Cara pakai: kirim `id` helicopter di path. + +### 5. Get helicopter by ID +- `GET /api/v1/helicopters/get/{id}` +- Fungsi: ambil detail helicopter berdasarkan `id`. +- Cara pakai: kirim `id` helicopter di path. + +### 6. Get all helicopters +- `GET /api/v1/helicopters/get-all` +- Fungsi: list helicopter dengan filter, sort, dan pagination. +- Cara pakai: + - `search` atau `filter[search]` + - `sort` + - `page[number]`, `page[size]` + - `page`, `size`, `limit` + +### 7. Get all helicopters (datatable) +- `GET /api/v1/helicopters/get-all/dt` +- Fungsi: list helicopter untuk datatable. +- Cara pakai: gunakan parameter pagination datatable yang didukung backend. + +## Bases APIs + +### 1. Create base +- `POST /api/v1/bases/create` +- Fungsi: membuat base baru. +- Cara pakai: kirim payload base create sesuai Swagger. + +### 2. Update base +- `PATCH /api/v1/bases/update/{uuid}` +- Fungsi: update sebagian field base. +- Cara pakai: kirim `uuid` base di path, lalu field yang ingin diubah. + +### 3. Delete base +- `DELETE /api/v1/bases/delete/{uuid}` +- Fungsi: soft delete base. +- Cara pakai: kirim `uuid` base di path. + +### 4. Get base by ID +- `GET /api/v1/bases/get/{uuid}` +- Fungsi: ambil detail base. +- Cara pakai: kirim `uuid` base di path. + +### 5. Get all bases +- `GET /api/v1/bases/get-all` +- Fungsi: list base dengan filter, sort, dan pagination. +- Cara pakai: + - `search` atau `filter[search]` + - `sort` + - `page[number]`, `page[size]` + - `page`, `size`, `limit` + +### 6. Get all bases (datatable) +- `GET /api/v1/bases/get-all/dt` +- Fungsi: list base untuk datatable. +- Cara pakai: gunakan parameter pagination datatable yang didukung backend. + +## Contact APIs + +### 1. Create contact +- `POST /api/v1/contacts/create` +- Fungsi: membuat user contact baru beserta profile role-specific. +- Cara pakai: + - `data.type` harus `contact_create` + - `role_ids` menentukan role + - setiap role yang dipilih harus punya object profile yang sesuai di `attributes` + +### 2. Update contact +- `PATCH /api/v1/contacts/{user_id}` +- Fungsi: update contact. +- Cara pakai: + - `data.type` harus `contact_update` + - bisa update `role_id` dan `role_ids` + - `role_ids: []` akan menghapus semua role + - hanya satu profile role-specific boleh ada di `attributes` + +### 3. Get contact +- `GET /api/v1/contacts/{user_id}` +- Fungsi: ambil detail contact. +- Cara pakai: kirim `user_id` di path. + +### 4. Get all contacts +- `GET /api/v1/contacts/get-all` +- Fungsi: list contact dengan filter role, pilot category, sort, dan pagination. +- Cara pakai: + - filter role / pilot category sesuai kebutuhan frontend + - sort field seperti `name`, `email`, `is_active`, `created_at`, `updated_at`, dan lainnya + +### 5. Get all contacts (datatable) +- `GET /api/v1/contacts/get-all/dt` +- Fungsi: list contact untuk datatable. +- Cara pakai: gunakan parameter pagination datatable yang didukung backend. + +### 6. Delete contact +- `DELETE /api/v1/contacts/{user_id}` +- Fungsi: soft delete contact. +- Cara pakai: kirim `user_id` di path. + +### 7. Update contact active status +- `PATCH /api/v1/contacts/{user_id}/status` +- Fungsi: update status aktif contact. +- Cara pakai: kirim `user_id` di path dan payload status. + +### 8. Update contact role +- `PATCH /api/v1/contacts/{user_id}/role` +- Fungsi: update role contact. +- Cara pakai: kirim `user_id` di path dan payload role baru. + +### 9. Update pilot category +- `PATCH /api/v1/contacts/{user_id}/pilot-category` +- Fungsi: update pilot category. +- Cara pakai: kirim `user_id` di path dan payload kategori. + +### 10. Update contact license fields +- `PATCH /api/v1/contacts/{user_id}/license` +- Fungsi: update data lisensi contact. +- Cara pakai: kirim `user_id` di path dan payload field lisensi. + +### 11. Update chief flags +- `PATCH /api/v1/contacts/{user_id}/chief-flags` +- Fungsi: update flag chief. +- Cara pakai: kirim `user_id` di path dan payload flag yang ingin diubah. + +### 12. Update responsible complaints flag +- `PATCH /api/v1/contacts/{user_id}/responsible-complaints` +- Fungsi: update flag responsible complaints. +- Cara pakai: kirim `user_id` di path dan payload flag yang ingin diubah. + +### 13. Bulk update contacts status +- `POST /api/v1/contacts/bulk-update` +- Fungsi: update status banyak contact sekaligus. +- Cara pakai: kirim daftar contact yang ingin diupdate. + +## Flights API + +### Get all flights +- `GET /api/v1/flights/get-all` +- Fungsi: list flight dengan pagination, filter, sort, dan step workflow. +- Cara pakai: + - `search` atau `filter[search]` + - `date` atau `filter[date]` untuk filter tanggal flight + - `sort` + - `page[number]`, `page[size]` + - `page`, `size`, `limit` + +### Contoh response +```json +{ + "data": [ + { + "type": "flight", + "id": "019e8afa-e602-716c-8d2a-b6b11a89caf0", + "attributes": { + "status": "", + "mission_code": "M-060326-001", + "date": "2026-06-03", + "total_draft": 2, + "takeover": { + "id": "019e8afa-e5fe-7de0-ac5d-fb5ab95896b0", + "daily_inspection": { + "name": "test test", + "short_name": "", + "date": "05.06.2026", + "utc_time": "05:03" + } + }, + "steps": [ + { + "phase": "takeover", + "position": "takeover", + "step": 1, + "complete": true, + "id": "019e8afa-e5fe-7de0-ac5d-fb5ab95896b0", + "status": "completed", + "completed_at": "2026-06-03T00:55:59Z", + "completed_by": "test test" + }, + { + "phase": "mission", + "step": 2, + "complete": true, + "status": "assigned", + "mission_type": [ + { + "type": "CAT", + "draft": 2, + "completed": 0 + } + ], + "completed_at": "", + "completed_by": "" + } + ], + "draft": [ + { + "type": "CAT", + "items": [ + { + "id": "019e9640-f7af-7940-80e8-1e9a83013afb", + "type": "CAT", + "status": "draft", + "flight_data_id": "019e9640-f7b0-7787-b43c-0b395ff2792a" + }, + { + "id": "019e9640-f8cf-72b1-84dc-b50c1a3c2cc6", + "type": "CAT", + "status": "draft", + "flight_data_id": "019e9640-f8d0-7ea2-b250-a7e31bfba95f" + } + ] + } + ], + "created_at": "2026-06-03T00:55:59Z", + "created_by": "test test", + "updated_at": "2026-06-03T00:55:59Z", + "updated_by": "test test" + } + } + ], + "meta": { + "page_number": 1, + "page_size": 20, + "total": 3 + } +} +``` + +## Takeover APIs + +### 1. Create takeover +- `POST /api/v1/takeovers/create` +- Fungsi: membuat takeover secara atomik. +- Cara pakai: + - `base_id`, `duty_date`, `helicopter_id` + - `inspection` + - `roster_detail` + - `notes` +- Catatan: + - `shift_date` dan `shift_time` sudah tidak dipakai lagi di takeover request. + - shift diambil dari `base`. + +### 2. Update takeover +- `PATCH /api/v1/takeovers/update/{id}` +- Fungsi: replace takeover berdasarkan flight UUID. +- Cara pakai: + - kirim `id` takeover / flight di path + - body mengikuti format takeover update yang sama dengan create + +### 3. Get takeover by ID +- `GET /api/v1/takeovers/get/{id}` +- Fungsi: ambil takeover detail. +- Cara pakai: kirim `id` di path. + +### 4. Get all takeovers +- `GET /api/v1/takeovers/get-all` +- Fungsi: list takeover composite yang dibangun dari flight, reserve AC, duty roster, dan inspection. +- Cara pakai: tidak ada parameter wajib. + +### 5. Delete takeover +- `DELETE /api/v1/takeovers/delete/{id}` +- Fungsi: delete takeover berdasarkan flight UUID. +- Cara pakai: kirim `id` di path. + +### Payload create/update takeover +```json +{ + "data": { + "attributes": { + "base_id": "string", + "duty_date": "string", + "helicopter_id": "string", + "inspection": { + "before": { + "file_checklist": [ + { + "helicopter_file_id": "019d7000-aaaa-7bbb-8ccc-111111111111", + "is_done": true + } + ], + "fuel_added_amount": 0, + "fuel_amount": 0, + "fuel_unit": "string", + "hydraulic_lh_checked": true, + "hydraulic_rh_checked": true, + "note": "string", + "oil_engine_nr1_checked": true, + "oil_engine_nr2_checked": true, + "oil_transmission_igb_checked": true, + "oil_transmission_mgb_checked": true, + "oil_transmission_tgb_checked": true + }, + "inspection_date": "2026-04-15", + "prepare": { + "file_checklist": [ + { + "helicopter_file_id": "019d7000-aaaa-7bbb-8ccc-111111111111", + "is_done": true + } + ], + "notam_briefing": true, + "operational_flight_plan": true, + "weather_briefing": true + } + }, + "notes": "string", + "roster_detail": { + "doctor": [ + { + "id": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + ], + "helper": [ + { + "id": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + ], + "other_person": [ + { + "email": "guest@example.com", + "mobile_phone": "+43-1234", + "name": "Guest Person" + } + ], + "pilot": [ + { + "co_pilot": true, + "crew_type": "main", + "examiner": true, + "flight_instructor": true, + "id": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d", + "line_checker": true, + "supervisor": true + } + ], + "rescuer": [ + { + "id": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + ] + } + }, + "type": "takeover_create" + } +} +``` + +### Response takeover detail +```json +{ + "data": { + "type": "takeover", + "id": "019e8713-aa19-7fb7-a510-a0332dc69697", + "attributes": { + "id": "019e8713-aa19-7fb7-a510-a0332dc69697", + "flight_id": "019e8afa-e602-716c-8d2a-b6b11a89caf0", + "reserve_ac_id": "019e8713-aa19-7fb7-a510-a0332dc69698", + "flight_inspection_id": "019e8713-aa19-7fb7-a510-a0332dc69699", + "shift_start": "05:00:00", + "shift_end": "13:00:00", + "base_id": "019d61db-53c6-7280-87ec-e65f205c6d3b", + "base": { + "id": "019d61db-53c6-7280-87ec-e65f205c6d3b", + "name": "Base Name" + }, + "duty_date": "2026-06-03", + "helicopter_id": "019d6771-5fb5-7337-837f-bc5ed85181a1", + "helicopter": { + "id": "019d6771-5fb5-7337-837f-bc5ed85181a1", + "identifier": "D-HYAR", + "designation": "AIRBUS H145", + "type": "H145", + "aog": false, + "is_active": true + }, + "notes": "string", + "roster_detail": { + "pilot": [], + "doctor": [], + "rescuer": [], + "helper": [], + "other_person": [] + }, + "inspection": { + "before": {}, + "prepare": {}, + "inspection_date": "2026-06-03" + } + } + } +} +``` + +## Notes + +- `takeover` request tidak memakai `shift_date` dan `shift_time`. +- `flights/get-all` menampilkan step `takeover`, `mission`, dan `after_flight_inspection` kalau record after inspection sudah ada. +- `mission.step` ditampilkan sebagai `step: 2`. +- `complete` selalu ada di response step; kalau belum selesai nilainya `false`. +- `mission_type.completed` masih angka, karena itu hitungan draft/completed, bukan boolean. diff --git a/docs/AUTH_SSO_FLOW.md b/docs/AUTH_SSO_FLOW.md new file mode 100644 index 0000000..6dd05f0 --- /dev/null +++ b/docs/AUTH_SSO_FLOW.md @@ -0,0 +1,96 @@ +# AUTH SSO FLOW (Frontend) + +## 1) New SSO Login Flow +1. Frontend calls `GET /api/v1/auth/microsoft/login`. +2. Redirect user to `data.attributes.url`. +3. Provider redirects to backend callback `GET /api/v1/auth/microsoft/callback?code=...&state=...`. +4. Backend checks existing SSO mapping (`provider=microsoft` + `provider_subject`). +5. Result: +- Mapping + user exists: login success, cookies/session issued. +- Mapping not found: login rejected (`401`). +- Mapping exists but user missing: login rejected (`404`). + +## 2) Important Rule +SSO **does not create new account automatically**. User must already exist and be linked first. + +## 3) Link SSO (for logged-in existing user) +Endpoint: `POST /api/v1/auth/sso/link` + +Auth: required (same auth cookie/session as normal login). + +Request: +```json +{ + "data": { + "type": "auth_sso_link", + "attributes": { + "provider": "microsoft", + "code": "" + } + } +} +``` + +Success `200`: +```json +{ + "data": { + "type": "auth_sso_link", + "attributes": { + "provider": "microsoft", + "provider_subject": "00000000-0000-0000-0000-000000000000", + "email": "user@example.com" + } + } +} +``` + +## 4) Unlink SSO +Endpoint: `DELETE /api/v1/auth/sso/unlink` + +Auth: required. + +Request: +```json +{ + "data": { + "type": "auth_sso_unlink", + "attributes": { + "provider": "microsoft" + } + } +} +``` + +Success `200`: +```json +{ + "data": { + "type": "auth_sso_unlink", + "attributes": { + "provider": "microsoft", + "unlinked": true + } + } +} +``` + +## 5) Endpoint Summary +- `GET /api/v1/auth/microsoft/login` +- `GET /api/v1/auth/microsoft/callback` +- `POST /api/v1/auth/sso/link` +- `DELETE /api/v1/auth/sso/unlink` + +## 6) Error Handling (Frontend) +Handle these API errors explicitly: +- `401 Unauthorized` +- `403 Forbidden` +- `404 user not found` +- `401 sso not linked` +- `409 sso already linked` +- `409 sso linked to another user` + +Recommended behavior: +- `401 sso not linked`: show message to login manually first, then link SSO in account settings. +- `409` errors on link: inform user mapping conflict and stop retry loop. +- `404 user not found`: force logout and ask user to re-authenticate. diff --git a/docs/aog-mcf-nsr-flow.md b/docs/aog-mcf-nsr-flow.md new file mode 100644 index 0000000..a5ddee4 --- /dev/null +++ b/docs/aog-mcf-nsr-flow.md @@ -0,0 +1,379 @@ +# Panduan Status Helikopter: AOG, MCF, NSR & Cara Menerbangkannya Lagi + +> Dokumen ini menjelaskan **kapan sebuah helikopter berhenti boleh terbang**, **kenapa**, dan **langkah apa yang harus dilakukan supaya boleh terbang lagi** — lengkap dengan endpoint yang dipakai di tiap langkah. +> +> Ditulis supaya bisa dimengerti baik oleh tim operasional maupun developer. Bagian teknis (endpoint, field) ada di tiap langkah, tapi alurnya diceritakan dulu dengan bahasa sederhana. +> +> Semua endpoint berawalan `/api/v1` dan butuh login. + +--- + +## 1. Istilah Penting (versi gampang) + +Sebelum masuk alur, kenali dulu pemainnya: + +| Istilah | Bahasa gampangnya | +|---|---| +| **Complaint** | Laporan kerusakan/keluhan pada helikopter. "Ada yang rusak nih." | +| **MEL** (Minimum Equipment List) | Aturan: kerusakan ini boleh ditunda berapa lama sebelum pesawat **wajib** berhenti. Ada kelas A/B/C/D (makin besar = makin lama boleh ditunda). | +| **NON-MEL** | Kerusakan yang **tidak boleh ditunda sama sekali** → pesawat langsung berhenti. | +| **AOG** (Aircraft On Ground) | Status "pesawat dikandangkan, **tidak boleh terbang**". | +| **NSR** (Non-Safety Release) | Keputusan: "kerusakan ini aman ditunda, **boleh terbang dulu**" walau belum diperbaiki. | +| **Action Taken** | Tindakan perbaikan sementara yang dicatat, supaya pesawat boleh terbang lagi — tapi **belum dianggap selesai resmi**. | +| **EASA Release** (CRS Part-145) | Surat resmi "**pesawat sudah diperbaiki & layak terbang**". Inilah yang benar-benar **menutup** kerusakan. | +| **MCF** (Maintenance Check Flight) | Penerbangan uji setelah perbaikan besar. Selama wajib MCF, pesawat berstatus khusus. | +| **Fleet Status** | Data kondisi tiap helikopter: jadwal perawatan, jam terbang, status, dll. | + +**Inti yang paling sering bikin bingung** (akan diulang lagi nanti): +> **Boleh terbang lagi** ≠ **kerusakan selesai.** +> - Pesawat bisa **boleh terbang lagi** lewat *Action Taken* atau *NSR* — tapi laporannya **masih terbuka**. +> - Kerusakan baru **benar-benar selesai (ditutup)** ketika **EASA Release ditandatangani (sign)** — bukan sekadar dibuat. + +--- + +## 2. Status Helikopter & Urutannya + +Helikopter selalu ada di salah satu dari 4 status ini. Status **dihitung otomatis** dari kondisi data (bukan diisi manual): + +| Status | Artinya | +|---|---| +| `mcf` | Wajib penerbangan uji (Maintenance Check Flight) dulu | +| `aog` | Dikandangkan, tidak boleh terbang | +| `booked` | Sedang dipakai misi | +| `available` | Sehat, siap terbang | + +**Kalau beberapa kondisi terjadi bersamaan, yang menang urutannya begini:** + +``` +MCF > AOG > Booked > Available +``` + +Contoh: helikopter yang sedang wajib MCF **dan** punya kerusakan AOG → tetap tampil `mcf` (karena MCF paling tinggi). Ini sama persis dengan sistem Wucher lama. + +--- + +## 3. Gambaran Besar: 3 Jalan Sebuah Helikopter Berhenti Terbang + +Ada **3 sebab** helikopter jadi tidak boleh terbang. Tiap sebab punya cara keluar sendiri: + +| Sebab berhenti | Jadi status | Cara terbang lagi | +|---|---|---| +| **A. Ada kerusakan** (complaint) | `aog` | EASA Release disign (atau ditunda dulu via Action/NSR) | +| **B. Jadwal perawatan jatuh tempo** (otomatis) | `aog` | Perbarui jadwal/selesai inspeksi → hitung ulang | +| **C. Wajib penerbangan uji** (MCF) | `mcf` | Selesaikan MCF dengan hasil "passed" | + +Tiga sebab ini dibahas satu per satu di bawah. + +--- + +## 4. JALAN A — Helikopter Rusak (Complaint → AOG → Perbaikan) + +Ini alur paling sering. Ceritanya berurutan: + +### Langkah 1 — Catat kerusakannya +Begitu ada kerusakan, buat laporan (complaint). + +`POST /complaints/create` (permission `complaint.create`) + +```jsonc +{ + "data": { + "type": "complaint_create", + "attributes": { + "helicopter_id": "019d6771-5fb5-7337-837f-bc5ed85181a1", + "description": "Getaran di Engine 2 saat cruise", + "mel_severity": 0, // lihat tabel di Langkah 2 + "aircraft_hours_at_report": "1245.3" + } + } +} +``` + +### Langkah 2 — Tentukan seberapa parah (klasifikasi MEL) +Field `mel_severity` menentukan **kapan pesawat harus berhenti**: + +| `mel_severity` | Kelas | Boleh terbang sampai kapan? | +|---|---|---| +| `0` | NON-MEL | **Tidak boleh sama sekali** → AOG seketika | +| `1` | A | Maksimal **1 hari**, lewat itu → AOG | +| `2` | B | Maksimal **3 hari** → AOG | +| `3` | C | Maksimal **10 hari** → AOG | +| `4` | D | Maksimal **120 hari** → AOG | +| *(dikosongkan)* | belum diklasifikasi | Statusnya `pending_classification`, **belum bikin AOG** sampai diklasifikasi | + +> Klasifikasi bisa diisi belakangan lewat `PATCH /complaints/update/:id` (field `mel_severity`). Sistem mencatat siapa & kapan mengklasifikasi (audit `mel_classified_by/at`). + +Selama kerusakan ini bikin AOG, di Fleet Status detail helikopter akan **muncul** data `complaints` + `easa`. Kalau sudah tidak AOG, dua data itu hilang sendiri. + +### Langkah 3 (opsional) — Mau terbang dulu sebelum diperbaiki tuntas? +Kadang pesawat perlu tetap operasi sambil menunggu perbaikan resmi. Ada **2 cara**, dan ini beda fungsi: + +**Cara 3a — Action Taken (tindakan sementara sudah dilakukan):** +`PATCH /complaints/update/:id` isi `action_taken`. + +```jsonc +{ "data": { "type": "complaint_update", "id": "", + "attributes": { + "action_taken": "Kencangkan engine mount, cek torsi OK", + "aircraft_hours_at_fix": "1250.1" + } } } +``` +→ Pesawat **boleh terbang lagi**, tapi laporan **masih terbuka**. + +**Cara 3b — NSR (kerusakan aman ditunda):** +`PATCH /complaints/update/:id` isi `is_nsr: true` (**butuh permission khusus `complaint.nsr`**). + +```jsonc +{ "data": { "type": "complaint_update", "id": "", + "attributes": { + "is_nsr": true, + "nsr_reason": "Dirilis di bawah MEL, defect ditunda" + } } } +``` +→ Pesawat **boleh terbang lagi**, laporan **masih terbuka**. Sistem mencatat siapa yang memutuskan NSR (`nsr_decided_by/at`). + +> **Beda Action Taken vs NSR:** +> - *Action Taken* = "sudah saya tangani sementara". +> - *NSR* = "belum ditangani, tapi memang **boleh ditunda** dengan aman". +> Keduanya sama-sama bikin pesawat boleh terbang lagi, dan **sama-sama belum menutup laporan**. +> +> Membatalkan NSR: kirim `is_nsr: false` → aturan AOG berlaku lagi. + +### Langkah 4 — Tutup kerusakan secara resmi (EASA Release) +Inilah langkah yang **benar-benar menyelesaikan** kerusakan. Tiga sub-langkah: + +**4a. Buat surat EASA-nya** (boleh draft / belum lengkap dulu): +`POST /easa-releases/create` (permission `easa_release.create`) + +> ⚠️ **Syarat boleh membuat EASA:** semua complaint terbuka helikopter itu **harus sudah di-*Action Taken* atau di-NSR dulu** (Langkah 3). Kalau masih ada yang "mentah", create ditolak (`ErrEASAReleaseComplaintsPendingAction`). Jadi Langkah 3 itu **pintu masuk** ke Langkah 4. + +**4b. Lengkapi datanya** (kalau tadi masih draft): +`PATCH /easa-releases/update/:id` + +**4c. TANDA TANGAN (sign) — ini momen kerusakan benar-benar ditutup:** +`POST /easa-releases/sign/:id` (permission `easa_release.sign`, **tanpa body** — penandatangan = user yang sedang login). + +```bash +POST /api/v1/easa-releases/sign/019d6772-471c-7b70-b42a-d94020ef61e4 +``` + +**Sebelum bisa sign, 5 field ini WAJIB terisi:** + +| Field | Contoh | +|---|---| +| `easa_date` | `2026-06-16` | +| `easa_ac_hours` | `1245.3` | +| `easa_location` | `LOWI` | +| `easa_wo_no` | `WO-2026-001` | +| `easa_signer_id` | `145.A.50` | + +Kalau ada yang kosong → ditolak (`ErrEASAReleaseIncompleteForSign`) beserta daftar `missing_sign_fields`. Aturannya tegas: **EASA yang belum lengkap tidak boleh dianggap selesai.** + +> **Jalan pintas:** kalau datanya sudah lengkap dari awal, kamu bisa langsung sign **saat create** — tambahkan `"sign": true` di body create. (Catatan: flag `sign` ini **hanya ada di create**, tidak ada di update.) + +**Begitu sign berhasil, otomatis terjadi:** +1. Surat EASA tercatat `signed_at` + `signed_by`. +2. **Semua complaint terbuka** helikopter itu ditutup → statusnya jadi `serviced`. +3. Karena tidak ada lagi kerusakan yang menggantung → **AOG hilang**, data `complaints`/`easa` di Fleet Status ikut hilang. +4. Tercatat di riwayat (history): "Release to Service (EASA signed)". + +### Ringkasan Jalan A (urut) +``` +Rusak → buat complaint → klasifikasi MEL + → (opsional) Action Taken / NSR ➜ boleh terbang, laporan MASIH terbuka + → EASA create → EASA lengkapi → EASA SIGN ✅ ➜ laporan DITUTUP, AOG hilang +``` + +--- + +## 5. JALAN B — Jadwal Perawatan Jatuh Tempo (AOG Otomatis) + +### Apa ini? +Tiap helikopter punya jadwal inspeksi di Fleet Status (mis. inspeksi tiap 100 jam, atau jatuh tempo di tanggal tertentu). Kalau **jam terbang** atau **tanggal** sudah melewati batas, sistem **otomatis** men-AOG-kan pesawat — tanpa perlu ada yang lapor. + +### Cara masuk AOG +Diatur di Fleet Status, tiap jadwal punya: +- `due` = batas normal (boleh berupa **jam**, mis. `1200`, atau **tanggal**, mis. `2026-09-01`) +- `ext` = batas perpanjangan (kalau diisi, ini yang dipakai menggantikan `due`) + +Saat jam/tanggal aktual melewati batas → status jadi `aog` dengan keterangan otomatis (`AOG: ... next due reached`). + +> Engine yang tidak aktif di helikopter (flag `NR1`/`NR2` = false) **dilewati** — jadwal engine itu tidak ikut bikin AOG. + +### Kapan sistem menghitung ulang? +AOG jenis ini **disimpan**, jadi perlu pemicu untuk berubah: +- Saat data penerbangan (flight data) dibuat/diubah/dihapus. +- Saat Fleet Status helikopter itu di-update. +- **Otomatis tiap 1 jam** (cron) — supaya AOG berbasis tanggal tetap nyala walau tidak ada aktivitas. + +### Cara terbang lagi +Perbarui jadwalnya supaya tidak lagi lewat tempo (mis. setelah inspeksi selesai, isi `due`/`ext` yang baru): + +`PATCH /fleet-status/update/:id` +```jsonc +{ "data": { "type": "fleet_status_update", "id": "", + "attributes": { "maintenance_schedules": [ + { "inspection_type": "100H", "due": "1400", "ext": "" } + ] } } } +``` +Pada perhitungan ulang berikutnya, AOG otomatis ini dilepas. (Catatan: sistem hanya melepas AOG yang dibuatnya sendiri secara otomatis; AOG manual tidak disentuh.) + +--- + +## 6. JALAN C — Wajib Penerbangan Uji (MCF) + +### Apa ini? +Setelah pekerjaan perawatan besar, helikopter kadang wajib menjalani **penerbangan uji (MCF)** dulu sebelum dianggap normal. Selama wajib MCF, statusnya `mcf` (mengalahkan AOG). + +### Langkah 1 — Tandai wajib MCF (cukup minimal) +`POST /mcf/create` (permission `mcf.create`) — body cukup `helicopter_id` saja (+ `notes` opsional). Tujuannya hanya menandai pesawat ini wajib MCF. + +```jsonc +{ "data": { "type": "mcf_create", + "attributes": { + "helicopter_id": "019d6771-5fb5-7337-837f-bc5ed85181a1", + "notes": "Wajib MCF setelah kerja engine mount" + } } } +``` + +### Langkah 2 — Selesaikan MCF setelah terbang uji +`POST /mcf/complete/:helicopter_id` (permission `mcf.complete`) — **pakai helicopter_id**, bukan id MCF. Wajib isi semua field hasil uji: + +```jsonc +{ "data": { "type": "mcf_complete", + "attributes": { + "af_hours": "1245.3", + "landing_count": 3, + "utc_time": "2026-06-17T08:30:00Z", // format RFC3339 + "date": "2026-06-17", // format YYYY-MM-DD + "result": "passed", // passed atau failed + "notes": "Getaran dalam batas normal" + } } } +``` + +**Hasilnya menentukan:** + +| `result` | Yang terjadi | +|---|---| +| `passed` | MCF beres → pesawat **lepas dari status MCF** (kembali ke status lain sesuai kondisi) | +| `failed` | Pesawat **tetap MCF**, hasil gagal dicatat ke riwayat → ulangi sampai `passed` | + +> Catatan: kalau tidak ada MCF terbuka untuk helikopter itu, complete ditolak (`409`). + +--- + +## 7. Melihat Riwayat (History) + +Semua kejadian penting di atas — dari kerusakan dilaporkan, diklasifikasi, NSR, perbaikan, EASA disign, hasil MCF, sampai AOG otomatis nyala/mati — **tercatat otomatis** dalam satu garis waktu (timeline) per helikopter. Berguna untuk audit: *siapa* melakukan *apa* dan *kapan*. + +### Di mana lihatnya? +`GET /fleet-status/history/:helicopter_id` (permission `flight.read`) + +```bash +GET /api/v1/fleet-status/history/019d6771-5fb5-7337-837f-bc5ed85181a1?page=1&page_size=20 +``` + +- Diurutkan **terbaru di atas**. +- Mendukung halaman: `page` (default 1) & `page_size` (default 20). +- Timeline ini **gabungan dua sumber**: catatan perawatan yang diselesaikan (service log) + semua kejadian aksi (complaint, EASA, MCF, AOG). + +### Kejadian apa saja yang tercatat? + +| `action` | Tercatat ketika… | +|---|---| +| `complaint_filed` | Laporan kerusakan dibuat | +| `mel_classified` | Kerusakan diklasifikasi MEL (A/B/C/D atau NON-MEL) | +| `nsr_done` | NSR diberikan (boleh terbang walau defect terbuka) | +| `nsr_cancelled` | NSR dibatalkan | +| `action_taken` | Tindakan perbaikan sementara dicatat | +| `easa_created` | Surat EASA dibuat (draft) | +| `easa_signed` | **EASA ditandatangani → kerusakan ditutup** | +| `mcf_result` | Hasil MCF diisi (`passed`/`failed`) | +| `aog_set` | AOG otomatis dinyalakan (jadwal jatuh tempo) | +| `aog_cleared` | AOG otomatis dilepas | +| `serviced` | Inspeksi/perawatan diselesaikan | + +### Bentuk responsnya + +Tiap baris timeline berisi: kapan, jenisnya, aksinya, pesan, dan **siapa pelakunya** (nama langsung tersedia di `actor_name`). + +```jsonc +{ + "data": [ + { + "at": "2026-06-17T10:15:00Z", + "entity_type": "easa_release", + "entity_id": "019d6772-471c-7b70-b42a-d94020ef61e4", + "action": "easa_signed", + "message": "Release to Service (EASA signed)", + "actor": "019e91e6-2908-7f37-93e6-015ed1b7587d", + "actor_name": "John Doe" + }, + { + "at": "2026-06-17T09:40:00Z", + "entity_type": "complaint", + "action": "action_taken", + "message": "Action taken — Kencangkan engine mount, cek torsi OK", + "actor_name": "Jane Tech" + } + ], + "meta": { "page_number": 1, "page_size": 20, "total": 12 } +} +``` + +> Selain timeline ini, daftar **kerusakan per helikopter** (beserta status terkini, NSR, action, dan EASA terkait) bisa dilihat lewat `GET /complaints/get-by-helicopter/:helicopter_id`, dan kondisi terkini helikopter (status + complaint + EASA aktif) lewat `GET /fleet-status/get-by-helicopter/:helicopter_id`. + +--- + +## 8. Tabel Endpoint Ringkas + +| Mau apa | Endpoint | Permission | +|---|---|---| +| Lapor kerusakan | `POST /complaints/create` | `complaint.create` | +| Klasifikasi MEL / Action Taken / NSR | `PATCH /complaints/update/:id` | `complaint.update` (+`complaint.nsr` utk NSR) | +| Lihat kerusakan per helikopter | `GET /complaints/get-by-helicopter/:helicopter_id` | `complaint.read` | +| Buat surat EASA | `POST /easa-releases/create` | `easa_release.create` | +| Lengkapi EASA | `PATCH /easa-releases/update/:id` | `easa_release.update` | +| **Tanda tangan EASA (tutup kerusakan)** | `POST /easa-releases/sign/:id` | `easa_release.sign` | +| Tandai wajib MCF | `POST /mcf/create` | `mcf.create` | +| **Selesaikan MCF** | `POST /mcf/complete/:helicopter_id` | `mcf.complete` | +| Atur jadwal perawatan & jam | `POST /fleet-status/create` · `PATCH /fleet-status/update/:id` | `flight.create` / `flight.update` | +| Lihat status + complaint + EASA | `GET /fleet-status/get-by-helicopter/:helicopter_id` | `flight.read` | +| Lihat riwayat helikopter | `GET /fleet-status/history/:helicopter_id` | `flight.read` | + +--- + +## 9. Tanya–Jawab Cepat (FAQ) + +**T: Kerusakan dianggap selesai saat EASA dibuat atau saat disign?** +J: Saat **disign**. Membuat (create) atau melengkapi (update) EASA **belum** menutup apa pun. Penutupan complaint terjadi tepat di endpoint `sign`. + +**T: Bedanya Action Taken, NSR, dan EASA Sign?** +J: +- **Action Taken** & **NSR** → bikin pesawat **boleh terbang lagi**, tapi laporan **masih terbuka**. +- **EASA Sign** → **menutup** laporan secara resmi (status `serviced`). + +**T: Kalau cuma isi Action Taken, kapan laporannya hilang dari daftar AOG?** +J: AOG-nya langsung hilang begitu Action Taken diisi (pesawat boleh terbang). Tapi laporannya **tetap tercatat terbuka** sampai EASA disign. + +**T: Kenapa saya tidak bisa membuat EASA?** +J: Kemungkinan masih ada complaint yang belum di-Action/NSR. Selesaikan Langkah 3 dulu untuk tiap complaint terbuka. + +**T: Helikopter wajib MCF tapi juga ada kerusakan AOG, tampil yang mana?** +J: Tampil `mcf`, karena MCF prioritas tertinggi. + +**T: AOG karena jadwal jatuh tempo kok tidak langsung hilang setelah saya update jadwal?** +J: AOG jenis ini dihitung ulang saat ada flight data baru, update fleet status, atau otomatis tiap 1 jam. Tunggu pemicu berikutnya. + +**T: Di mana saya bisa lihat riwayat apa saja yang sudah terjadi pada satu helikopter?** +J: Di `GET /fleet-status/history/:helicopter_id` — timeline lengkap (terbaru di atas) berisi siapa melakukan apa & kapan. Lihat §7. + +--- + +## 10. Catatan untuk Developer + +- **AOG karena kerusakan** dihitung *saat dibaca* (real-time dari complaint terbuka) → berubah instan, tidak perlu job. +- **AOG karena jadwal** *disimpan* di helikopter → perlu pemicu (flight data / update fleet / cron 1 jam). Hanya AOG otomatis yang dilepas otomatis; AOG manual tidak. +- **MCF mengalahkan AOG** dalam prioritas status. +- **Penutupan kerusakan hanya lewat EASA sign** — `action_taken` & `is_nsr` hanya mengangkat larangan terbang, tidak menutup laporan. +- **Masa tenggang MEL dihitung dalam HARI** (A=1, B=3, C=10, D=120) di `internal/domain/complaint/model.go` fungsi `MELDeadline` (`* 24 * time.Hour`). diff --git a/docs/audit-log.md b/docs/audit-log.md new file mode 100644 index 0000000..3ed8d04 --- /dev/null +++ b/docs/audit-log.md @@ -0,0 +1,49 @@ +# Audit Log + +## Tujuan + +- Menyimpan jejak request API dan event penting service ke tabel `audit_logs`. +- Bersifat append-only. +- Tidak menyimpan secret sensitif (password, raw token). + +## Desain + +- Semua route di bawah `/api/*` dicatat otomatis oleh middleware `AuditMiddleware`. +- Penyimpanan ke DB dilakukan async melalui `AuditLogService` (buffered queue + worker) agar request API tetap cepat. +- Jika queue penuh, request tetap lanjut (fail-open) dan sistem menulis warning log. + +## Schema Ringkas (`audit_logs`) + +- `id` (BINARY(16), PK) +- `request_id` (indexed) +- `actor_user_id` (nullable, indexed) +- `layer` (`api` / `service`) +- `action` (indexed) +- `method`, `path`, `status_code`, `success` +- `ip`, `user_agent` +- `message` +- `metadata` (JSON) +- `created_at` + +## Helper Pemakaian di Service + +Gunakan helper yang sudah ada di `AuthService`: + +```go +s.logServiceAudit(ctx, "auth.reset_password.consume", userID, true, "password_reset_success", map[string]any{ + "reason": "manual_reset", +}) +``` + +Untuk service lain, inject `*service.AuditLogService` lalu panggil: + +```go +auditSvc.Log(ctx, service.AuditLogInput{ + Layer: "service", + Action: "your.service.action", + ActorUserID: userID, + Success: true, + Message: "done", + Metadata: map[string]any{"key": "value"}, +}) +``` diff --git a/docs/auth-flow.md b/docs/auth-flow.md new file mode 100644 index 0000000..55910db --- /dev/null +++ b/docs/auth-flow.md @@ -0,0 +1,272 @@ +# Auth Flow + +This document describes the recommended authentication flows in Wucher. All API responses follow **JSON:API**. + +## 1) Email/Password Registration + +### 1.1 Register +`POST /api/v1/auth/register` + +Notes: +- The user role is assigned based on `AUTH_DEFAULT_ROLE`. +- For production, you can disable this endpoint by setting `AUTH_DISABLE_REGISTER=true`. + +Request: +```json +{ + "data": { + "type": "auth_register", + "attributes": { + "email": "user@example.com", + "password": "StrongP@ssw0rd", + "first_name": "John", + "last_name": "Doe", + "mobile_phone": "" + } + } +} +``` + +Response: `201 Created` with user resource. + +### 1.2 Verify Email +User receives a verification link in email: +`GET /api/v1/auth/verify-email?token=...` + +Response: `200 OK` with `{ verified: true }`. + +### 1.3 Login +`POST /api/v1/auth/login` + +If TOTP **is not enabled**, response is `200 OK` and cookies are set: +- `wucher_at` (access) +- `wucher_rt` (refresh) + +If TOTP **is enabled**, response is `202 Accepted` with `challenge_token`. + +### 1.4 Forgot Password +`POST /api/v1/auth/forgot-password` + +Request: +```json +{ + "data": { + "type": "auth_forgot_password", + "attributes": { + "email": "user@example.com" + } + } +} +``` + +Response is always `200 OK` with a generic message: +`If the account exists, a password reset link has been sent.` + +### 1.5 Reset Password +`POST /api/v1/auth/reset-password` + +Request: +```json +{ + "data": { + "type": "auth_reset_password", + "attributes": { + "token": "", + "new_password": "StrongPassphrase123!", + "confirm_password": "StrongPassphrase123!" + } + } +} +``` + +Success response: `200 OK` with message instructing the user to log in again. + +Invalid/expired/used token response: `400 Bad Request` with: +`This reset link is invalid or expired.` + +### 1.6 Security PIN for Sensitive Actions + +Security PIN endpoints: +- `POST /api/v1/auth/pin/setup` +- `POST /api/v1/auth/pin/verify` +- `POST /api/v1/auth/pin/change` +- `POST /api/v1/auth/pin/forgot` +- `POST /api/v1/auth/pin/reset` + +For `POST /api/v1/auth/pin/reset`: +- If `method=password`, send the reset `token` from the email link plus `password`. +- If `method=microsoft`, send only `reauth_token` from Microsoft re-auth. + +Sensitive actions require header: +- `X-PIN-Verification-Token: ` + +`auth/pin/verify` menggunakan `action` berupa UUID opaque (transaction ID), bukan permission key seperti `user.delete`. + +Detailed flow + full payload examples: +- See [docs/security-pin.md](security-pin.md) + +## 2) TOTP Setup (Enable 2FA) + +### 2.1 Setup +`POST /api/v1/auth/totp/setup` + +Request: +```json +{ + "data": { + "type": "auth_totp_setup", + "attributes": { + "user_id": "", + "email": "user@example.com" + } + } +} +``` + +Response: `200 OK` with `secret` and `otpauth_url`. +> TOTP is **not** enabled yet (pending). + +### 2.2 Confirm +`POST /api/v1/auth/totp/confirm` + +Request: +```json +{ + "data": { + "type": "auth_totp_confirm", + "attributes": { + "user_id": "", + "code": "123456" + } + } +} +``` + +Response: `200 OK` and TOTP becomes **enabled**. + +## 3) TOTP Login (2FA) + +### 3.1 Login +`POST /api/v1/auth/login` + +Response: `202 Accepted` with `challenge_token`. + +### 3.2 Verify +`POST /api/v1/auth/totp/verify` + +Request: +```json +{ + "data": { + "type": "auth_totp_verify", + "attributes": { + "challenge_token": "", + "code": "123456" + } + } +} +``` + +Response: `200 OK` and cookies are set. + +## 4) Microsoft Entra SSO + +### 4.1 Start Login +`GET /api/v1/auth/microsoft/login` + +Response: `200 OK` with JSON:API body (`type: auth_microsoft_url`) containing `url` and `redirect_url` + +### 4.2 Callback +Microsoft redirects to: +`GET /api/v1/auth/microsoft/callback?code=...&state=...` + +Backend behavior: +- If SSO mapping (`provider + provider_subject`) is linked to an existing user, login succeeds. +- If mapping is missing, login is rejected (`401 SSO not linked`). +- If mapping exists but user record is missing, login is rejected (`404 user not found`). +- No auto-register on SSO callback. + +On success, backend sets cookies and may redirect to: +`AUTH_SSO_SUCCESS_REDIRECT` + +### 4.3 Link SSO (authenticated user) +`POST /api/v1/auth/sso/link` + +Body: +```json +{ + "data": { + "type": "auth_sso_link", + "attributes": { + "provider": "microsoft", + "code": "" + } + } +} +``` + +Rules: +- 1 user only 1 SSO mapping. +- Same SSO account cannot be linked to another user. + +### 4.4 Unlink SSO (authenticated user) +`DELETE /api/v1/auth/sso/unlink` + +Body: +```json +{ + "data": { + "type": "auth_sso_unlink", + "attributes": { + "provider": "microsoft" + } + } +} +``` + +## 5) WebAuthn (Passkey) + +### 5.1 Register Credential (Authenticated User) +1. `POST /api/v1/auth/webauthn/register/begin` (auth required) +2. Browser executes `navigator.credentials.create(...)` using `public_key` from response +3. `POST /api/v1/auth/webauthn/register/finish` with `challenge_token` + `credential` + +### 5.2 Login with WebAuthn +1. `POST /api/v1/auth/webauthn/login/begin` with email +2. Browser executes `navigator.credentials.get(...)` using `public_key` from response +3. `POST /api/v1/auth/webauthn/login/finish` with `challenge_token` + `credential` +4. On success, backend sets auth cookies. + +## 6) Refresh & Logout + +### Refresh +`POST /api/v1/auth/refresh` + +Uses refresh cookie to issue new tokens. + +### Logout +`POST /api/v1/auth/logout` + +Revokes refresh token and clears cookies. + +Frontend note: +- Use `credentials: 'include'` on requests that depend on auth cookies. +- Keep app logout local-only; do not redirect to Microsoft logout from this endpoint. +- Microsoft front-channel logout is handled separately at `/api/v1/auth/microsoft/front-channel-logout` and is designed to work in an iframe. + +## Notes +- Access token is stored in HttpOnly cookie: `AUTH_JWT_ACCESS_COOKIE` +- Refresh token is stored in HttpOnly cookie: `AUTH_JWT_REFRESH_COOKIE` +- Cookies are configured by `AUTH_JWT_COOKIE_*` envs +- Microsoft SSO sessions use `SameSite=None; Secure` so front-channel iframe logout can receive cookies. +- Access token TTL is controlled by `AUTH_JWT_ACCESS_TTL` (default `10m`) +- Refresh token TTL is controlled by `AUTH_JWT_REFRESH_TTL` (default `1h`) +- TOTP challenge tokens are stored server-side in MySQL-backed runtime storage for `AUTH_TOTP_CHALLENGE_TTL` +- Password-reset tokens are stored in MySQL as SHA-256 hashes (raw token is never stored) +- Password-reset tokens are single-use and expire by `AUTH_PASSWORD_RESET_TTL` (default 20m) +- Forgot-password endpoint has per-IP and per-email throttling +- Login (including SSO callback and post-TOTP token issue) bumps per-user session version, so a new login invalidates previous active sessions +- Successful password reset revokes active sessions by bumping per-user session version +- PIN can be blocked after max failed attempts; use `POST /api/v1/auth/pin/request-reset` (auth required) to request reset email for blocked accounts +- WebAuthn challenge token is single-use, stored server-side, and expires by `AUTH_WEBAUTHN_CHALLENGE_TTL`. +- WebAuthn credential verification enforces RP ID + RP origin checks from `AUTH_WEBAUTHN_RP_ID` and `AUTH_WEBAUTHN_RP_ORIGINS`. diff --git a/docs/base-contact-roles-frontend-flow.md b/docs/base-contact-roles-frontend-flow.md new file mode 100644 index 0000000..4a6fda5 --- /dev/null +++ b/docs/base-contact-roles-frontend-flow.md @@ -0,0 +1,146 @@ +# Base Contact Roles Frontend Flow (English) + +This document explains the frontend flow for contact assignment in the **Bases** module: +- `responsible_flight_rescuer_contact_ids` (multi-contact) +- `chief_physician_in_charge_contact_ids` (multi-contact) + +Contact source is always based on **contact id (`users.id`)**. + +## 1) Data Source for Contact Dropdowns + +Get contact candidates from contacts API: +- `GET /api/v1/contacts/get-all?role=air_rescuer&limit=200` + +Minimum fields for FE: +- `id` (resource id) => send this in base payload +- `attributes.first_name`, `attributes.last_name`, `attributes.short_name` (display label) +- optional: `attributes.is_active` + +UI suggestion: +- Use 2 separate multi-select components: + - Responsible Flight Rescuer + - Chief Physician In Charge +- The same person may exist in both lists if business rules allow it. + +## 2) Create Base Flow + +Endpoint: +- `POST /api/v1/bases/create` + +Example payload: +```json +{ + "data": { + "type": "base", + "attributes": { + "base": "Lude", + "base_category": "regular", + "responsible_flight_rescuer_contact_ids": [ + "0196a111-1111-7abc-9def-111111111111", + "0196a222-2222-7abc-9def-222222222222" + ], + "chief_physician_in_charge_contact_ids": [ + "0196a333-3333-7abc-9def-333333333333" + ] + } + } +} +``` + +Backend behavior: +- each id must be a valid UUID and exist in `users` +- invalid UUID / missing user id => request fails (`4xx`) + +## 3) Update Base Flow + +Endpoint: +- `PATCH /api/v1/bases/update/{uuid}` + +Payload to update assignment only: +```json +{ + "data": { + "type": "base_update", + "attributes": { + "responsible_flight_rescuer_contact_ids": [ + "0196a111-1111-7abc-9def-111111111111" + ], + "chief_physician_in_charge_contact_ids": [] + } + } +} +``` + +Important notes: +- update uses **replace** semantics per base (not append) +- always submit the final arrays from current UI state +- send `[]` to clear all assignments for that role + +## 4) Load Base Detail into Edit Form + +Endpoint: +- `GET /api/v1/bases/get/{uuid}` + +Response fields used by FE: +- `data.attributes.responsible_flight_rescuer_contact_ids` +- `data.attributes.chief_physician_in_charge_contact_ids` +- `data.attributes.responsible_flight_rescuers` (id + first_name + last_name) +- `data.attributes.chief_physicians_in_charge` (id + first_name + last_name) + +FE flow: +1. Load base detail. +2. Load contact options for dropdown. +3. Map selected ids to option objects for selected state. +4. Use detailed arrays to show human-readable names immediately. + +## 5) Show Base Roles in Contacts Module + +Contacts API now includes: +- `attributes.base_roles` (array) + +Item format: +```json +{ + "base_id": "0196b111-1111-7abc-9def-111111111111", + "base_name": "Lude", + "role_code": "responsible_flight_rescuer" +} +``` + +Affected endpoints: +- `GET /api/v1/contacts/get-all` +- `GET /api/v1/contacts/get-all/dt` +- `GET /api/v1/contacts/{user_id}` + +Suggested label mapping in UI: +- `responsible_flight_rescuer` => `Responsible Flight Rescuer` +- `chief_physician_in_charge` => `Chief Physician In Charge` + +## 6) UX Recommendations + +- In contact detail page, group `base_roles` by `base_name`. +- In contact list, show compact chips, e.g.: + - `Lude (Responsible Flight Rescuer)` + - `Berlin (Chief Physician In Charge)` +- For large lists, show only first N chips + `+X more`. + +## 7) Minimum Error Handling + +- If base submit fails because UUID validation fails: + - show general message: `Invalid contact assignment`. +- If contact id no longer exists: + - show message: `Some contacts are no longer available, please refresh`. +- After successful create/update: + - refresh base detail to keep assignment state synced with backend. + +## 8) Compatibility Note + +For backward compatibility, both ID arrays and detailed name arrays are returned: +- `responsible_flight_rescuer_contact_ids` +- `responsible_flight_rescuers` +- `chief_physician_in_charge_contact_ids` +- `chief_physicians_in_charge` + +Recommended FE usage: +- Use `..._contact_ids` for submit payload. +- Use detailed arrays for display names. diff --git a/docs/collections/Wucher.json b/docs/collections/Wucher.json new file mode 100644 index 0000000..eb93535 --- /dev/null +++ b/docs/collections/Wucher.json @@ -0,0 +1,980 @@ +{ + "info": { + "name": "Wucher", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Role", + "item": [ + { + "name": "Create", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/create", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "create" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"attributes\": {\n \"description\": \"Testing.\",\n \"name\": \"role_test\"\n },\n \"type\": \"role\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Delete", + "request": { + "method": "DELETE", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/delete/:uuid", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "delete", + ":id" + ], + "query": [], + "variable": [ + { + "key": "id", + "value": "019c31eb-5750-7c71-b6b9-042282c61bab" + } + ] + } + } + }, + { + "name": "Get All DT", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/get-all/dt?draw=1&length=5&start", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "get-all", + "dt?draw=1&length=5&start" + ], + "query": [ + { + "key": "draw", + "value": "1" + }, + { + "key": "length", + "value": "5" + }, + { + "key": "start", + "value": "" + }, + { + "key": "search", + "value": "sta" + } + ], + "variable": [] + } + } + }, + { + "name": "Get All", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/get-all", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "get-all" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Get By ID", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/get/:uuid", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "get", + ":id" + ], + "query": [], + "variable": [ + { + "key": "id", + "value": "019c31eb-5750-7c71-b6b9-042282c61bab" + } + ] + } + } + }, + { + "name": "Update", + "request": { + "method": "PATCH", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/update/:uuid", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "update", + ":id" + ], + "query": [], + "variable": [ + { + "key": "id", + "value": "019c31eb-5750-7c71-b6b9-042282c61bab" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"attributes\": {\n \"description\": \"Testing update.\",\n \"name\": \"test_role\"\n },\n \"id\": \"019c31eb-5750-7c71-b6b9-042282c61bab\",\n \"type\": \"role\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Permission", + "item": [ + { + "name": "Assign", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/:uuid/permissions/assign-bulk", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + ":uuid", + "permissions", + "assign-bulk" + ], + "query": [], + "variable": [ + { + "key": "uuid", + "value": "" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"role_assign_permission_bulk\",\n \"attributes\": {\n \"permission_ids\": [\n \"019c79bd-c7a5-703d-8b7d-cd935e7e5316\",\n \"019c79bd-c7a5-720e-ad50-989d8848e37c\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Get All Permission", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/permissions/get-all", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + "permissions", + "get-all" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Permission of Role", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/:uuid/permissions", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + ":uuid", + "permissions" + ], + "query": [], + "variable": [ + { + "key": "uuid", + "value": "019c30e6-1b8a-7714-898a-6e954e3dad33" + } + ] + } + } + }, + { + "name": "Remove Permission", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/roles/:uuid/permissions/remove-bulk", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "roles", + ":uuid", + "permissions", + "remove-bulk" + ], + "query": [], + "variable": [ + { + "key": "uuid", + "value": "019c30e6-1b8a-7714-898a-6e954e3dad33" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"role_remove_permission_bulk\",\n \"attributes\": {\n \"permission_ids\": [\n \"019c79bd-c7a5-7974-ba89-69d52f0f3712\",\n \"019c79bd-c7a5-7339-add2-5a1c900cf1c3\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + } + ] + } + ] + }, + { + "name": "User", + "item": [ + { + "name": "Create", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/users/create", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "users", + "create" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"user\",\n \"attributes\": {\n \"email\": \"marcomelandri808@gmail.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"mobile_phone\": \"+628123456789\",\n \"role_id\": \"019c30e6-1b8a-7714-898a-6e954e3dad33\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Delete", + "request": { + "method": "DELETE", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/users/delete/:uuid", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "users", + "delete", + ":uuid" + ], + "query": [], + "variable": [ + { + "key": "uuid", + "value": "019c79da-8c32-785c-b547-beee3b321e9b" + } + ] + } + } + }, + { + "name": "Get All DT", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/users/get-all/dt", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "users", + "get-all", + "dt" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Get All", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/users/get-all", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "users", + "get-all" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Get By UUID", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/users/get/:uuid", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "users", + "get", + ":uuid" + ], + "query": [], + "variable": [ + { + "key": "uuid", + "value": "019c7940-c4c2-7fa4-ae0b-4eeb9e75295b" + } + ] + } + } + }, + { + "name": "Update", + "request": { + "method": "PATCH", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/users/update/:uuid", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "users", + "update", + ":uuid" + ], + "query": [], + "variable": [ + { + "key": "uuid", + "value": "019c7940-c4c2-7fa4-ae0b-4eeb9e75295b" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"user\",\n \"id\": \"019c7940-c4c2-7fa4-ae0b-4eeb9e75295b\",\n \"attributes\": {\n \"first_name\": \"Johnny\",\n \"last_name\": \"Doe\",\n \"mobile_phone\": \"+628123456700\",\n \"role_id\": \"019c30e6-1b8a-7714-898a-6e954e3dad33\"\n }\n }\n}\n", + "options": { + "raw": { + "language": "json" + } + } + } + } + } + ] + }, + { + "name": "Auth", + "item": [ + { + "name": "Auth Me", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/me", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "me" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Invite", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/invite", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "invite" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"auth_invite\",\n \"attributes\": {\n \"email\": \"marcomelandri808@gmail.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"mobile_phone\": \"+628123456789\",\n \"role_id\": \"019c30e6-1b8a-7714-898a-6e954e3dad33\"\n }\n }\n}\n", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Login", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/login", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "login" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"auth_login\",\n \"attributes\": {\n \"email\": \"marcomelandri808@gmail.com\",\n \"password\": \"StrongP@ssw0rd!\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Logout", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/logout", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "logout" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Refresh Token", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/refresh", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "refresh" + ], + "query": [], + "variable": [] + } + } + }, + { + "name": "Register", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/register", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "register" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"auth_register\",\n \"attributes\": {\n \"email\": \"innovatexsh@gmail.com\",\n \"password\": \"securePass123\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"mobile_phone\": \"087782553442\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "Set New Password", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/set-password", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "set-password" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"type\": \"auth_set_password\",\n \"attributes\": {\n \"token\": \"--TpCbfl1JEpEJq1jr2l1XeFggiAN7Z2k23pwMVrxac\",\n \"password\": \"StrongP@ssw0rd!\"\n }\n }\n}\n", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "SSO", + "item": [ + { + "name": "MS Login", + "request": { + "method": "GET", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/microsoft/login", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "microsoft", + "login" + ], + "query": [], + "variable": [] + } + } + } + ] + }, + { + "name": "TOTP", + "item": [ + { + "name": "TOTP Disable", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/totp/disable", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "totp", + "disable" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"attributes\": {\n \"user_id\": \"019c2e21-d880-7b9a-89ac-3d3d8593502f\"\n },\n \"type\": \"auth_totp_disable\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "TOTP Setup", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/totp/setup", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "totp", + "setup" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"attributes\": {\n \"email\": \"innovatexsh@gmail.com\",\n \"user_id\": \"019c2e21-d880-7b9a-89ac-3d3d8593502f\"\n },\n \"type\": \"auth_totp_setup\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "TOTP Verify Login", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/totp/verify", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "totp", + "verify" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"attributes\": {\n \"challenge_token\": \"Z_2A0lm5IekOiIBPbpiJzpuevFYW6oxEIgdMZjPkcpw\",\n \"code\": \"568754\"\n },\n \"type\": \"auth_totp_verify\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + }, + { + "name": "TOTP Verify", + "request": { + "method": "POST", + "header": [], + "auth": { + "type": "noauth" + }, + "description": "", + "url": { + "raw": "{{baseUrl}}/api/v1/auth/totp/confirm", + "protocol": "", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "auth", + "totp", + "confirm" + ], + "query": [], + "variable": [] + }, + "body": { + "mode": "raw", + "raw": "{\n \"data\": {\n \"attributes\": {\n \"code\": \"898427\",\n \"user_id\": \"019c2e21-d880-7b9a-89ac-3d3d8593502f\"\n },\n \"type\": \"auth_totp_confirm\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + } + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "default" + } + ] +} diff --git a/docs/contact-create-flow.md b/docs/contact-create-flow.md new file mode 100644 index 0000000..cdc28f3 --- /dev/null +++ b/docs/contact-create-flow.md @@ -0,0 +1,359 @@ +# Contact Create Flow (Frontend Guide) + +This document explains the full flow to create a contact, send an automatic invite email, and let the invited user set their first password. + +## 1) Prerequisites + +1. Admin user is already logged in and has an `access token`. +2. Admin user has already configured a Security PIN. +3. Frontend knows the target `role_id` (fetch from `GET /api/v1/roles/get-all`). + +## 2) Endpoints Used + +1. `POST /api/v1/auth/pin/verify` to get a PIN `action_token`. +2. `POST /api/v1/contacts/create` to create the contact (auto invite enabled). +3. `POST /api/v1/auth/set-password` to set the first password from invite token. + +## 3) Detailed Flow + +### Step A - Verify PIN for contact create action + +Action ID for contact create: +- `2a59e8b4-5a72-4be3-b8fc-4f49941181c3` + +Request: + +```json +{ + "data": { + "type": "auth_pin_verify", + "attributes": { + "pin": "123456", + "action": "2a59e8b4-5a72-4be3-b8fc-4f49941181c3" + } + } +} +``` + +Success response: + +```json +{ + "data": { + "type": "auth_pin_verify", + "attributes": { + "verified": true, + "action_token": "", + "action_token_ttl_ms": 300000 + } + } +} +``` + +Store `action_token`, then pass it to contact create in header: +- `X-PIN-Verification-Token: ` + +### Step B - Create Contact (auto invite) + +Endpoint: +- `POST /api/v1/contacts/create` + +Required headers: +- `Authorization: Bearer ` +- `X-PIN-Verification-Token: ` +- `Content-Type: application/json` + +Minimum required body: +- `data.type = contact__create` +- `attributes.role_id` +- `attributes.email` +- `attributes.first_name` +- `attributes.last_name` + +Allowed create types: +- `contact_pilot_create` +- `contact_doctor_create` +- `contact_air_rescuer_create` +- `contact_technician_create` +- `contact_flight_assistant_create` +- `contact_staff_create` + +Rules: +- `data.type` must match the role resolved from `attributes.role_id`. +- Only one role-specific profile object is allowed, and it must match `data.type`. + +> Note: backend now **automatically sends an invite email** after successful contact creation. + +If PIN token is missing/invalid, API returns `202` with PIN verification required error. + +### Step C - User clicks invite link from email + +Invite email contains a link like: +- `https://frontend-domain/set-password?token=` + +This token already carries user identity (email + user id claims) and is single-use. + +### Step D - Frontend submits set password + +Endpoint: +- `POST /api/v1/auth/set-password` + +Request: + +```json +{ + "data": { + "type": "auth_set_password", + "attributes": { + "token": "", + "password": "StrongP@ssw0rd123" + } + } +} +``` + +If successful, user can directly log in using `POST /api/v1/auth/login`. + +## 4) Contact Create Payloads by Role + +## Pilot + +```json +{ + "data": { + "type": "contact_pilot_create", + "attributes": { + "role_id": "", + "email": "pilot1@example.com", + "username": "pilot1", + "first_name": "Marco", + "last_name": "Melandri", + "mobile_phone": "+628123456789", + "timezone": "Asia/Jakarta", + "is_active": true, + "pilot": { + "pilot_category": "regular", + "short_name": "Marco", + "license_no": "PIL-001", + "license_issued_at": "2025-01-01T00:00:00Z", + "license_expired_at": "2028-01-01T00:00:00Z", + "technician_license_no": "TECH-001", + "has_ifr_qualification": true, + "is_chief_pilot": false, + "is_dul": false, + "total_flight_minutes": 12000, + "responsible_pilot_minutes": 5000, + "second_pilot_minutes": 3000, + "double_command_minutes": 1000, + "flight_instructor_minutes": 500, + "night_flight_minutes": 1200, + "ifr_flight_minutes": 800, + "location": "Jakarta", + "postcode": "12950", + "street_line": "Jl. Sudirman No. 1", + "weight_kg": 72.5, + "photo_file_id": "/uploads/contacts/pilot1.jpg" + } + } + } +} +``` + +## Doctor + +```json +{ + "data": { + "type": "contact_doctor_create", + "attributes": { + "role_id": "", + "email": "doctor1@example.com", + "username": "doctor1", + "first_name": "Alicia", + "last_name": "Pratama", + "mobile_phone": "+628123450001", + "timezone": "Asia/Jakarta", + "is_active": true, + "doctor": { + "short_name": "Dr. Alicia", + "medical_license_no": "MED-DOCTOR-001", + "license_issued_at": "2024-01-01T00:00:00Z", + "license_expired_at": "2029-01-01T00:00:00Z", + "specialization": "Emergency Medicine", + "sub_specialization": "Trauma", + "is_chief_physician": false, + "location": "Jakarta", + "postcode": "12950", + "street_line": "Jl. Jenderal Sudirman No. 10", + "photo_file_id": "/uploads/contacts/doctor1.jpg" + } + } + } +} +``` + +## Air Rescuer + +```json +{ + "data": { + "type": "contact_air_rescuer_create", + "attributes": { + "role_id": "", + "email": "airrescuer1@example.com", + "username": "airrescuer1", + "first_name": "Raka", + "last_name": "Wijaya", + "mobile_phone": "+628123450002", + "timezone": "Asia/Jakarta", + "is_active": true, + "air_rescuer": { + "short_name": "Raka", + "is_chief_physician_in_charge": false, + "responsible_flight_rescuer": false, + "location": "Bandung", + "postcode": "40115", + "street_line": "Jl. Merdeka No. 20", + "photo_file_id": "/uploads/contacts/airrescuer1.jpg" + } + } + } +} +``` + +## Technician + +```json +{ + "data": { + "type": "contact_technician_create", + "attributes": { + "role_id": "", + "email": "tech1@example.com", + "username": "tech1", + "first_name": "Bima", + "last_name": "Nugraha", + "mobile_phone": "+628123450003", + "timezone": "Asia/Jakarta", + "is_active": true, + "technician": { + "short_name": "Bima", + "license_no": "TECH-7788", + "is_chief_technician": false, + "is_responsible_complaints": true, + "location": "Surabaya", + "postcode": "60231", + "street_line": "Jl. Diponegoro No. 30", + "photo_file_id": "/uploads/contacts/tech1.jpg" + } + } + } +} +``` + +## Flight Assistant + +```json +{ + "data": { + "type": "contact_flight_assistant_create", + "attributes": { + "role_id": "", + "email": "fa1@example.com", + "username": "fa1", + "first_name": "Nadia", + "last_name": "Putri", + "mobile_phone": "+628123450004", + "timezone": "Asia/Jakarta", + "is_active": true, + "flight_assistant": { + "short_name": "Nadia", + "location": "Yogyakarta", + "postcode": "55281", + "street_line": "Jl. Kaliurang No. 15", + "photo_file_id": "/uploads/contacts/fa1.jpg" + } + } + } +} +``` + +## Staff + +```json +{ + "data": { + "type": "contact_staff_create", + "attributes": { + "role_id": "", + "email": "staff1@example.com", + "username": "staff1", + "first_name": "Dina", + "last_name": "Maharani", + "mobile_phone": "+628123450005", + "timezone": "Asia/Jakarta", + "is_active": true, + "staff": { + "short_name": "Dina", + "role_label": "Operations Staff", + "location": "Semarang", + "postcode": "50132", + "street_line": "Jl. Pandanaran No. 8", + "photo_file_id": "/uploads/contacts/staff1.jpg" + } + } + } +} +``` + +## 5) Example Create Contact Response + +```json +{ + "data": { + "type": "contact", + "id": "019ce67e-9289-731d-ae4c-0156902aabbc", + "attributes": { + "role_code": "doctor", + "role_name": "doctor", + "email": "doctor1@example.com", + "first_name": "Alicia", + "last_name": "Pratama", + "is_active": true, + "doctor": { + "short_name": "Dr. Alicia", + "medical_license_no": "MED-DOCTOR-001" + }, + "invite_sent": true, + "invite_error": "" + } + } +} +``` + +If invite sending fails (for example SES/SQS config issue): +- contact create is still successful, +- `invite_sent = false`, +- `invite_error` contains the error message. + +## 6) Error Cases Frontend Should Handle + +1. `202 PIN verification required` on contact create: + - call `/auth/pin/verify` again (Step A), then retry create with `X-PIN-Verification-Token`. +2. `422 Validation error`: + - check payload fields (invalid role_id, invalid email, etc). +3. `400` on create: + - typically business/constraint conflict. +4. `400` on set-password: + - token invalid/expired/used or password does not meet rules. + +## 7) Frontend Integration Summary + +1. Admin logs in. +2. Fetch `role_id` from roles list. +3. Verify PIN (`/auth/pin/verify` with contact-create action ID). +4. Create contact (`/contacts/create` + `X-PIN-Verification-Token`). +5. Wait for invited user to open invite email. +6. Set-password page reads `token` from query param. +7. Submit `/auth/set-password` with `{ token, password }`. +8. Redirect user to login page. diff --git a/docs/contacts_module.md b/docs/contacts_module.md new file mode 100644 index 0000000..bf52f2a --- /dev/null +++ b/docs/contacts_module.md @@ -0,0 +1,129 @@ +# Contacts Module (HEMS) + +## Endpoint listing +- `GET /api/v1/contacts?role=pilot` +- `GET /api/v1/contacts?role=pilot&pilot_category=freelance` +- `GET /api/v1/contacts?role=pilot&pilot_category=dry_lease` +- `GET /api/v1/contacts?role=doctor` +- `GET /api/v1/contacts?role=air_rescuer` +- `GET /api/v1/contacts?role=technician` +- `GET /api/v1/contacts?role=flight_assistant` +- `GET /api/v1/contacts?role=staff` + +## Sensitive action flow (PIN) +1. Client memanggil endpoint sensitif. +2. API balas `202` dan memberi tahu `action` PIN yang dibutuhkan. +3. Client verifikasi PIN ke `/api/v1/auth/pin/verify`. +4. Backend mengembalikan token verifikasi singkat. +5. Client kirim ulang request sensitif dengan header `X-PIN-Verification-Token`. + +## Contoh payload + +### 1) Create pilot +```json +{ + "data": { + "type": "contact_pilot_create", + "attributes": { + "role_id": "", + "email": "pilot1@hems.local", + "username": "pilot1", + "first_name": "Marco", + "last_name": "Pilot", + "mobile_phone": "+62811111111", + "timezone": "Asia/Jakarta", + "pilot": { + "pilot_category": "freelance", + "short_name": "M. Pilot", + "license_no": "LIC-P-1001", + "has_ifr_qualification": true, + "total_flight_minutes": 12500, + "location": "Jakarta" + } + } + } +} +``` + +### 2) Update doctor +```json +{ + "data": { + "type": "contact_doctor_update", + "attributes": { + "is_active": true, + "doctor": { + "short_name": "dr. Ana", + "medical_license_no": "DOC-7788", + "specialization": "Anesthesiology", + "is_chief_physician": false + } + } + } +} +``` + +Rule: +- `data.type` wajib mengikuti role, mis. `contact_pilot_create`, `contact_air_rescuer_create`, `contact_staff_update`. +- Hanya satu object profile role yang boleh dikirim, dan harus sesuai dengan `data.type`. + +### 3) PIN verify sebelum aksi sensitif +```json +{ + "data": { + "type": "auth_pin_verify", + "attributes": { + "pin": "123456", + "action": "31f8bb88-a95f-43be-8fd8-129e1355d6a5" + } + } +} +``` + +### 4) Response `202` saat PIN dibutuhkan +```json +{ + "data": { + "type": "pin_verification_required", + "attributes": { + "required": true, + "action": "31f8bb88-a95f-43be-8fd8-129e1355d6a5", + "method": "pin", + "status": 202 + } + } +} +``` + +### 5) List response ringkas +```json +{ + "data": { + "type": "contacts", + "attributes": { + "items": [ + { + "id": "0195f43e-5030-7d2a-8b8a-020063f5ab03", + "role_code": "pilot", + "role_name": "pilot", + "pilot_category": "dry_lease", + "username": "pilot_dl_1", + "email": "pilot.dl1@hems.local", + "first_name": "Rama", + "last_name": "Wijaya", + "short_name": "R. Wijaya", + "license_no": "LIC-P-9988", + "location": "Surabaya", + "mobile_phone": "+62822222222", + "is_active": true + } + ], + "meta": { + "total": 1, + "limit": 20, + "offset": 0 + } + } + } +} +``` diff --git a/docs/data-migration-wiki-ckeditor.html b/docs/data-migration-wiki-ckeditor.html new file mode 100644 index 0000000..05a7947 --- /dev/null +++ b/docs/data-migration-wiki-ckeditor.html @@ -0,0 +1,187 @@ +

Data Migration Wiki

+

+ This page is the living technical document for migration scope, decisions, limitations, and open tasks. + Update it for every migration wave. +

+ +

Scope

+
    +
  • Database schema migrations in migrations/.
  • +
  • Data migration SQL (legacy to current domain model).
  • +
  • Non-DB migration concerns: binary files (PDF/images), queue backlog, manual user actions.
  • +
+ +

Source of Truth

+
    +
  • Versioned DB migrations: migrations/*.sql.
  • +
  • Legacy helicopter import flow: +
      +
    • migrations/goose/20260414_helicopter_import_staging.sql
    • +
    • migrations/goose/20260415_helicopter_import_apply.sql
    • +
    • migrations/scripts/migration_helicopter.sql (deprecated helper note)
    • +
    +
  • +
  • Queue migration notes: docs/queue-sqs-migration.md.
  • +
  • File manager APIs and attachment model: +
      +
    • docs/file-manager-upload.md
    • +
    • docs/file-manager-attachment.md
    • +
    +
  • +
+ +

Implemented Data Migrations

+
    +
  1. + 20260326_move_responsible_flight_rescuer_to_air_rescuer_profiles.sql +
      +
    • Copies pilot_profiles.responsible_flight_rescuer into air_rescuer_profiles.responsible_flight_rescuer.
    • +
    • Drops old column from pilot_profiles.
    • +
    +
  2. +
  3. + 20260403_file_manager_file_lifecycle_phase1.sql +
      +
    • Normalizes legacy file lifecycle states (pending/uploading) into newer states (validated/processing).
    • +
    • Carries upload_error into failure_reason.
    • +
    +
  4. +
  5. + 20260415_helicopter_import_apply.sql +
      +
    • Imports rows from helicopter_old_import into helicopters.
    • +
    • Maintains old-to-new ID mapping via helicopter_old_id_map.
    • +
    • Explicitly non-reversible (Down keeps data as audit trail).
    • +
    +
  6. +
+ +

Known Migration Decisions and Gaps

+ +

Confirmed "Will Not Migrate" (Current Decision)

+
    +
  1. + Legacy helicopter columns intentionally ignored: +
      +
    • counter
    • +
    • service
    • +
    • disabled
    • +
    • visible
    • +
    +

    Decision is documented in migrations/scripts/migration_helicopter.sql.

    +
  2. +
+ +

Confirmed "Not Migrated Yet" (Open)

+
    +
  1. + Legacy SQS queue backlog +
      +
    • Current worker does not consume old SQS backlog.
    • +
    • Required action: drain/discard SQS backlog before/at cutover.
    • +
    • Source: docs/queue-sqs-migration.md.
    • +
    +
  2. +
  3. + Binary files (PDF/images) into new file manager storage +
      +
    • Current migrations create file manager tables and attachment relations, but no SQL migration imports binary objects or bulk-inserts legacy metadata into file_files/attachments.
    • +
    • This means legacy documents may need manual re-upload unless a dedicated import job is created.
    • +
    +
  4. +
  5. + Legacy photo_file_id references in contact profile tables +
      +
    • Contact profile schemas still include photo_file_id string columns.
    • +
    • No migration currently maps these into attachments + image_attachment_id/foto_attachment_id.
    • +
    +
  6. +
+ +

Manual Actions Required at Cutover

+
    +
  1. Validate which legacy PDFs/images must remain accessible on day 1.
  2. +
  3. For files without automated import, perform manual re-upload via file manager flow.
  4. +
  5. Re-link uploaded files to business references (attachments) and to entity-level attachment columns where relevant.
  6. +
  7. Obtain client sign-off for any files intentionally left unmigrated.
  8. +
+ +

Migration TODO Register

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDItemTypeStatusOwnerNotes
MIG-001Decide final strategy for legacy PDFs/images: automated import vs manual re-uploadDecisionOpenTBDBlocks final cutover checklist
MIG-002Produce inventory of all legacy file references and classify critical/non-criticalAnalysisOpenTBDInclude PDFs + photos
MIG-003If automation chosen, implement importer to populate file_files and attachments safelyEngineeringOpenTBDPrefer idempotent batch import with audit log
MIG-004Define mapping from legacy contact photo_file_id to new attachment modelEngineeringOpenTBDCovers pilot/doctor/air rescuer/technician/FA/staff
MIG-005Document and execute SQS backlog drain/discard plan before production cutoverOpsOpenTBDSee queue migration doc
MIG-006Create client-facing exception list for data that cannot/will not be migratedGovernanceOpenTBDMust be approved before go-live
+ +

Cutover Validation Checklist

+
    +
  1. Run DB migrations in target environment.
  2. +
  3. Run all required data migration scripts (including helicopter import flow if applicable).
  4. +
  5. Validate row counts and sample records for migrated domains.
  6. +
  7. Validate file accessibility for mandatory records (especially PDFs needed operationally).
  8. +
  9. Validate queue behavior post-cutover (pending to published, no legacy SQS dependency).
  10. +
  11. Record migration result, exceptions, and follow-up tickets on this page.
  12. +
+ +

Change Log

+
    +
  • 2026-04-17: Initial wiki created from current migration scripts and docs.
  • +
  • 2026-04-17: Added CKEditor-ready HTML version.
  • +
diff --git a/docs/data-migration-wiki.md b/docs/data-migration-wiki.md new file mode 100644 index 0000000..787ffee --- /dev/null +++ b/docs/data-migration-wiki.md @@ -0,0 +1,94 @@ +# Data Migration Wiki + +This page is the living technical document for migration scope, decisions, limitations, and open tasks. +Update it for every migration wave. +For CKEditor copy-paste, use the HTML version: `docs/data-migration-wiki-ckeditor.html`. + +## Scope + +- Database schema migrations in [`migrations/`](../migrations/). +- Data migration SQL (legacy to current domain model). +- Non-DB migration concerns: binary files (PDF/images), queue backlog, manual user actions. + +## Source of Truth + +- Versioned DB migrations: [`migrations/*.sql`](../migrations/). +- Legacy helicopter import flow: + - [`migrations/goose/20260414_helicopter_import_staging.sql`](../migrations/goose/20260414_helicopter_import_staging.sql) + - [`migrations/goose/20260415_helicopter_import_apply.sql`](../migrations/goose/20260415_helicopter_import_apply.sql) + - Deprecated helper note: [`migrations/scripts/migration_helicopter.sql`](../migrations/scripts/migration_helicopter.sql) +- Queue migration notes: [`docs/queue-sqs-migration.md`](./queue-sqs-migration.md) +- File manager APIs and attachment model: + - [`docs/file-manager-upload.md`](./file-manager-upload.md) + - [`docs/file-manager-attachment.md`](./file-manager-attachment.md) + +## Implemented Data Migrations + +1. `20260326_move_responsible_flight_rescuer_to_air_rescuer_profiles.sql` +- Copies `pilot_profiles.responsible_flight_rescuer` into `air_rescuer_profiles.responsible_flight_rescuer`. +- Drops old column from `pilot_profiles`. + +2. `20260403_file_manager_file_lifecycle_phase1.sql` +- Normalizes legacy file lifecycle states (`pending/uploading`) into newer states (`validated/processing`) and carries `upload_error` into `failure_reason`. + +3. `20260415_helicopter_import_apply.sql` +- Imports rows from `helicopter_old_import` into `helicopters`. +- Maintains old-to-new ID mapping via `helicopter_old_id_map`. +- Explicitly non-reversible (`Down` keeps data as audit trail). + +## Known Migration Decisions and Gaps + +### Confirmed "Will Not Migrate" (Current Decision) + +1. Legacy helicopter columns intentionally ignored: +- `counter` +- `service` +- `disabled` +- `visible` +- Decision is documented in [`migrations/scripts/migration_helicopter.sql`](../migrations/scripts/migration_helicopter.sql). + +### Confirmed "Not Migrated Yet" (Open) + +1. Legacy SQS queue backlog +- Current worker does not consume old SQS backlog. +- Required action: drain/discard SQS backlog before/at cutover. +- Source: [`docs/queue-sqs-migration.md`](./queue-sqs-migration.md). + +2. Binary files (PDF/images) into new file manager storage +- Current migrations create file manager tables and attachment relations, but no SQL migration imports binary objects or bulk-inserts legacy metadata into `file_files`/`attachments`. +- This means legacy documents may need manual re-upload unless a dedicated import job is created. + +3. Legacy `photo_file_id` references in contact profile tables +- Contact profile schemas still include `photo_file_id` string columns. +- No migration currently maps these into `attachments` + `image_attachment_id`/`foto_attachment_id`. + +## Manual Actions Required at Cutover + +1. Validate which legacy PDFs/images must remain accessible on day 1. +2. For files without automated import, perform manual re-upload via file manager flow. +3. Re-link uploaded files to business references (attachments) and to entity-level attachment columns where relevant. +4. Obtain client sign-off for any files intentionally left unmigrated. + +## Migration TODO Register + +| ID | Item | Type | Status | Owner | Notes | +|---|---|---|---|---|---| +| MIG-001 | Decide final strategy for legacy PDFs/images: automated import vs manual re-upload | Decision | Open | TBD | Blocks final cutover checklist | +| MIG-002 | Produce inventory of all legacy file references and classify critical/non-critical | Analysis | Open | TBD | Include PDFs + photos | +| MIG-003 | If automation chosen, implement importer to populate `file_files` and `attachments` safely | Engineering | Open | TBD | Prefer idempotent batch import with audit log | +| MIG-004 | Define mapping from legacy contact `photo_file_id` to new attachment model | Engineering | Open | TBD | Covers pilot/doctor/air rescuer/technician/FA/staff | +| MIG-005 | Document and execute Redis backlog drain/discard plan before production cutover | Ops | Open | TBD | See queue migration doc | +| MIG-006 | Create client-facing exception list for data that cannot/will not be migrated | Governance | Open | TBD | Must be approved before go-live | + +## Cutover Validation Checklist + +1. Run DB migrations in target environment. +2. Run all required data migration scripts (including helicopter import flow if applicable). +3. Validate row counts and sample records for migrated domains. +4. Validate file accessibility for mandatory records (especially PDFs needed operationally). +5. Validate queue behavior post-cutover (`pending` to `published`, no legacy Redis dependency). +6. Record migration result, exceptions, and follow-up tickets in this page. + +## Change Log + +- 2026-04-17: Initial wiki created from current migration scripts and docs. diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..5073bc8 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,38390 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/action-signoffs/get-by-flight/{flight_id}": { + "get": { + "description": "Returns the fleet-level (complaint-independent) corrective-action sign-off state for a helicopter — ` + "`" + `signed` + "`" + `, ` + "`" + `signed_at` + "`" + `, ` + "`" + `signed_by` + "`" + `. When none exists yet it returns an unsigned placeholder. A signed sign-off is what gates the EASA maintenance release.", + "produces": [ + "application/json" + ], + "tags": [ + "Complaints - Sign Off" + ], + "summary": "Get the current corrective-action sign-off for a helicopter", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ActionSignoffResponse" + } + } + } + } + }, + "/api/v1/action-signoffs/sign/{helicopter_id}": { + "post": { + "description": "Toggles the corrective-action sign-off for a helicopter. ` + "`" + `sign` + "`" + ` (optional, default true) chooses the direction: true signs, false unsigns. Send ` + "`" + `complaint_id` + "`" + ` to target a specific complaint's sign-off — signing it requires ` + "`" + `action_taken` + "`" + ` recorded (else 422 ` + "`" + `ACTION_SIGNOFF_ACTION_REQUIRED` + "`" + `), and 404 ` + "`" + `ACTION_SIGNOFF_COMPLAINT_NOT_FOUND` + "`" + ` if the complaint does not exist. Omit ` + "`" + `complaint_id` + "`" + ` for a fleet-wide (complaint-independent) sign-off. A signed sign-off is what allows an EASA maintenance release to be created for the helicopter.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Complaints - Sign Off" + ], + "summary": "Toggle the corrective-action sign-off (per complaint or fleet-wide)", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "path", + "required": true + }, + { + "description": "Optional complaint_id + note", + "name": "request", + "in": "body", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ActionSignoffSignRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ActionSignoffResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/after-flight-inspection/create": { + "post": { + "description": "Create after-flight inspection and AFI file checklist for a flight inspection.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - After Flights Inspection" + ], + "summary": "Create last flights inspection", + "parameters": [ + { + "description": "Create last flights inspection request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/after-flight-inspection/delete": { + "delete": { + "description": "Delete after-flight inspection and AFI checklist rows by ` + "`" + `flight_inspection_id` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - After Flights Inspection" + ], + "summary": "Delete last flights inspection", + "parameters": [ + { + "description": "Delete last flights inspection request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionDeleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/after-flight-inspection/get": { + "get": { + "description": "Get after-flight inspection detail and AFI file checklist by query ` + "`" + `inspection_id` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - After Flights Inspection" + ], + "summary": "Get last flights inspection", + "parameters": [ + { + "type": "string", + "description": "Flight Inspection UUID", + "name": "inspection_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/after-flight-inspection/update": { + "patch": { + "description": "Update after-flight inspection and AFI file checklist for a flight inspection.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - After Flights Inspection" + ], + "summary": "Update last flights inspection", + "parameters": [ + { + "description": "Update last flights inspection request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/create": { + "post": { + "description": "Create one or more checklist headers for a single base.\nRequest uses ` + "`" + `base_id` + "`" + `.\nEach checklist can be created with or without initial ` + "`" + `items` + "`" + `.\n` + "`" + `scope_code` + "`" + ` accepts persisted codes and English aliases. Stored codes remain: ` + "`" + `TA` + "`" + `, ` + "`" + `MO` + "`" + `, ` + "`" + `DI` + "`" + `, ` + "`" + `MI` + "`" + `, ` + "`" + `DO` + "`" + `, ` + "`" + `FR` + "`" + `, ` + "`" + `SA` + "`" + `, ` + "`" + `SO` + "`" + `, ` + "`" + `M10` + "`" + `, ` + "`" + `M15` + "`" + `, ` + "`" + `M20` + "`" + `, ` + "`" + `M25` + "`" + `, ` + "`" + `DS` + "`" + `.\nExample aliases: ` + "`" + `DAILY` + "`" + ` -\u003e ` + "`" + `TA` + "`" + `, ` + "`" + `TU/TUE/TUESDAY` + "`" + ` -\u003e ` + "`" + `DI` + "`" + `, ` + "`" + `WE/WED/WEDNESDAY` + "`" + ` -\u003e ` + "`" + `MI` + "`" + `, ` + "`" + `TH/THU/THURSDAY` + "`" + ` -\u003e ` + "`" + `DO` + "`" + `, ` + "`" + `SU/SUN/SUNDAY` + "`" + ` -\u003e ` + "`" + `SO` + "`" + `, ` + "`" + `DEADLINE` + "`" + ` -\u003e ` + "`" + `DS` + "`" + `.\n` + "`" + `CAT` + "`" + ` is a virtual overview tab and is not allowed for create/update payloads.\nDuplicate ` + "`" + `position` + "`" + ` is allowed for both checklist headers and checklist items.\nDisplay order is stable: checklist headers ` + "`" + `position ASC, title ASC` + "`" + `; checklist_items ` + "`" + `position ASC, name ASC` + "`" + `.\nRequest is transactional: if one checklist/item fails, the whole batch is rolled back.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Create air rescuer checklist (bulk)", + "parameters": [ + { + "description": "Bulk checklist create payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/delete/{uuid}": { + "delete": { + "description": "Delete one checklist header by UUID.\nRelated checklist items are removed together following current implementation behavior.", + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Delete air rescuer checklist", + "parameters": [ + { + "type": "string", + "description": "Checklist UUID", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistGroupedListResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/get-all": { + "get": { + "description": "Grouped by base and returned in ` + "`" + `data[].attributes.bases` + "`" + `.\nResponse includes ` + "`" + `tabs[]` + "`" + ` with default tab order: ` + "`" + `CAT` + "`" + `, ` + "`" + `TA` + "`" + `, ` + "`" + `MO` + "`" + `, ` + "`" + `DI` + "`" + `, ` + "`" + `MI` + "`" + `, ` + "`" + `DO` + "`" + `, ` + "`" + `FR` + "`" + `, ` + "`" + `SA` + "`" + `, ` + "`" + `SO` + "`" + `, ` + "`" + `M10` + "`" + `, ` + "`" + `M15` + "`" + `, ` + "`" + `M20` + "`" + `, ` + "`" + `M25` + "`" + `, ` + "`" + `DS` + "`" + `.\n` + "`" + `CAT` + "`" + ` is a virtual overview tab (not persisted in DB).\n` + "`" + `CAT.groups[]` + "`" + ` only includes non-empty persisted scopes and does not include ` + "`" + `checklist_items` + "`" + `.\nNormal tabs are always present. Empty normal tabs are explicitly returned as ` + "`" + `checklists: []` + "`" + `.\nScope display names: ` + "`" + `CAT=Category` + "`" + `, ` + "`" + `TA=Daily` + "`" + `, ` + "`" + `MO=Monday` + "`" + `, ` + "`" + `DI=Tuesday` + "`" + `, ` + "`" + `MI=Wednesday` + "`" + `, ` + "`" + `DO=Thursday` + "`" + `, ` + "`" + `FR=Friday` + "`" + `, ` + "`" + `SA=Saturday` + "`" + `, ` + "`" + `SO=Sunday` + "`" + `, ` + "`" + `M10=M10` + "`" + `, ` + "`" + `M15=M15` + "`" + `, ` + "`" + `M20=M20` + "`" + `, ` + "`" + `M25=M25` + "`" + `, ` + "`" + `DS=Deadline` + "`" + `.\nDuplicate ` + "`" + `position` + "`" + ` is allowed. Display order is stable: checklist headers ` + "`" + `position ASC, title ASC` + "`" + `; checklist_items ` + "`" + `position ASC, name ASC` + "`" + `.\nOptional filters: ` + "`" + `base_id` + "`" + `, ` + "`" + `category_type` + "`" + ` (` + "`" + `regular|hems` + "`" + `), ` + "`" + `base_name` + "`" + `, ` + "`" + `base_abbreviation` + "`" + `, ` + "`" + `scope_code` + "`" + ` (` + "`" + `CAT` + "`" + `/` + "`" + `OVERVIEW` + "`" + ` or persisted scope / aliases).", + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "List air rescuer checklist grouped by base", + "parameters": [ + { + "type": "string", + "description": "Filter by base UUID", + "name": "base_id", + "in": "query" + }, + { + "enum": [ + "regular", + "hems" + ], + "type": "string", + "description": "Filter by base category", + "name": "category_type", + "in": "query" + }, + { + "type": "string", + "description": "Filter by base name (contains, case-insensitive)", + "name": "base_name", + "in": "query" + }, + { + "type": "string", + "description": "Filter by base abbreviation (contains, case-insensitive)", + "name": "base_abbreviation", + "in": "query" + }, + { + "type": "string", + "description": "Filter by scope code. Allowed canonical: CAT, TA, MO, DI, MI, DO, FR, SA, SO, M10, M15, M20, M25, DS. English aliases are also accepted.", + "name": "scope_code", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistGroupedDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/get-all/dt": { + "get": { + "description": "Datatable variant for grouped air rescuer checklist data.\nSemantic data is the same as ` + "`" + `get-all` + "`" + `: grouped by base with ` + "`" + `tabs[]` + "`" + `, virtual ` + "`" + `CAT` + "`" + `, and normal tabs always present.\n` + "`" + `CAT.groups[]` + "`" + ` only includes non-empty persisted scopes and does not include ` + "`" + `checklist_items` + "`" + `.\nEmpty normal tabs are explicitly returned as ` + "`" + `checklists: []` + "`" + `.\nScope display names: ` + "`" + `CAT=Category` + "`" + `, ` + "`" + `TA=Daily` + "`" + `, ` + "`" + `MO=Monday` + "`" + `, ` + "`" + `DI=Tuesday` + "`" + `, ` + "`" + `MI=Wednesday` + "`" + `, ` + "`" + `DO=Thursday` + "`" + `, ` + "`" + `FR=Friday` + "`" + `, ` + "`" + `SA=Saturday` + "`" + `, ` + "`" + `SO=Sunday` + "`" + `, ` + "`" + `M10=M10` + "`" + `, ` + "`" + `M15=M15` + "`" + `, ` + "`" + `M20=M20` + "`" + `, ` + "`" + `M25=M25` + "`" + `, ` + "`" + `DS=Deadline` + "`" + `.\nDuplicate ` + "`" + `position` + "`" + ` is allowed. Display order is stable: checklist headers ` + "`" + `position ASC, title ASC` + "`" + `; checklist_items ` + "`" + `position ASC, name ASC` + "`" + `.\nOptional filters: ` + "`" + `base_id` + "`" + `, ` + "`" + `category_type` + "`" + ` (` + "`" + `regular|hems` + "`" + `), ` + "`" + `base_name` + "`" + `, ` + "`" + `base_abbreviation` + "`" + `, ` + "`" + `scope_code` + "`" + `.\nPagination/query controls: ` + "`" + `page` + "`" + `, ` + "`" + `size` + "`" + `, ` + "`" + `limit` + "`" + `, ` + "`" + `draw` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "List air rescuer checklist grouped by base (datatable)", + "parameters": [ + { + "type": "string", + "description": "Filter by base UUID", + "name": "base_id", + "in": "query" + }, + { + "enum": [ + "regular", + "hems" + ], + "type": "string", + "description": "Filter by base category", + "name": "category_type", + "in": "query" + }, + { + "type": "string", + "description": "Filter by base name (contains, case-insensitive)", + "name": "base_name", + "in": "query" + }, + { + "type": "string", + "description": "Filter by base abbreviation (contains, case-insensitive)", + "name": "base_abbreviation", + "in": "query" + }, + { + "type": "string", + "description": "Filter by scope code. Allowed canonical: CAT, TA, MO, DI, MI, DO, FR, SA, SO, M10, M15, M20, M25, DS. English aliases are also accepted.", + "name": "scope_code", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (max 100)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Datatable draw counter", + "name": "draw", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistGroupedDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/get/{uuid}": { + "get": { + "description": "Return one checklist detail by UUID.\nIncludes checklist header, base info, and ` + "`" + `items` + "`" + ` (` + "`" + `checklist_items` + "`" + `).", + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Get air rescuer checklist by UUID", + "parameters": [ + { + "type": "string", + "description": "Checklist UUID", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/items/delete/{item_uuid}": { + "delete": { + "description": "Delete one existing checklist item by ` + "`" + `item_uuid` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Delete one checklist item", + "parameters": [ + { + "type": "string", + "description": "Checklist item UUID", + "name": "item_uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/items/update/{item_uuid}": { + "patch": { + "description": "Update one existing checklist item by ` + "`" + `item_uuid` + "`" + `.\nThis endpoint only affects the selected item.\nDuplicate ` + "`" + `position` + "`" + ` is allowed.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Update one checklist item", + "parameters": [ + { + "type": "string", + "description": "Checklist item UUID", + "name": "item_uuid", + "in": "path", + "required": true + }, + { + "description": "Checklist item update payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/update/{uuid}": { + "patch": { + "description": "Update checklist header fields (` + "`" + `scope_code` + "`" + `, ` + "`" + `title` + "`" + `, ` + "`" + `position` + "`" + `) and/or append ` + "`" + `new_items` + "`" + `.\n` + "`" + `new_items` + "`" + ` is append-only. Existing items are not updated/deleted by this endpoint.\nItem update/delete lifecycle must use dedicated item endpoints.\n` + "`" + `scope_code` + "`" + ` accepts persisted codes and English aliases, but ` + "`" + `CAT` + "`" + ` (or ` + "`" + `OVERVIEW` + "`" + `) is not allowed for update payload.\nDuplicate ` + "`" + `position` + "`" + ` is allowed.\nRequest is transactional.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Update air rescuer checklist header and append new items", + "parameters": [ + { + "type": "string", + "description": "Checklist UUID", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "Checklist update payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/{uuid}/items/create": { + "post": { + "description": "Create one or more items for an existing checklist.\nParent checklist is taken from path ` + "`" + `uuid` + "`" + `.\nDuplicate ` + "`" + `position` + "`" + ` is allowed. Display order for checklist_items is ` + "`" + `position ASC, name ASC` + "`" + `.\nRequest is transactional: if one item fails, all item inserts in the batch are rolled back.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Create checklist items (bulk)", + "parameters": [ + { + "type": "string", + "description": "Checklist UUID", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "Bulk checklist item create payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/air-rescuer-checklist/{uuid}/items/reorder": { + "patch": { + "description": "Reorder one or more checklist items for the checklist in path ` + "`" + `uuid` + "`" + `.\nUse this endpoint to swap or reorder positions with a single PIN validation.\nDuplicate ` + "`" + `position` + "`" + ` values remain allowed by domain rules.\nRequest is transactional: if one item fails, all position updates are rolled back.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Air Rescuer Checklist" + ], + "summary": "Reorder checklist items in one request", + "parameters": [ + { + "type": "string", + "description": "Checklist UUID", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "Checklist item reorder payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/audit-logs/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting.", + "produces": [ + "application/json" + ], + "tags": [ + "Audit Logs" + ], + "summary": "List audit logs", + "parameters": [ + { + "type": "string", + "description": "Search by request_id/layer/action/method/path/message (contains)", + "name": "filter[search]", + "in": "query" + }, + { + "enum": [ + "POST", + "PUT", + "PATCH", + "DELETE", + "UPDATE", + "ALL", + "*" + ], + "type": "string", + "description": "Method filter; single/comma-separated values or ALL/*", + "name": "filter[method]", + "in": "query" + }, + { + "enum": [ + "POST", + "PUT", + "PATCH", + "DELETE", + "UPDATE", + "ALL", + "*" + ], + "type": "string", + "description": "Alias for filter[method]", + "name": "method", + "in": "query" + }, + { + "enum": [ + "air_rescuer_checklist", + "audit_log", + "auth", + "base", + "base_regular", + "base_hems", + "base_roster", + "branding", + "contact", + "duty_roster", + "facility", + "federal_state", + "file_manager", + "flight", + "flight_data", + "forces_present", + "health_insurance_company", + "helicopter", + "helicopter_file", + "hems_roster", + "hospital", + "icao", + "insurance_patient_data", + "land", + "master_setting", + "medicine", + "mission", + "opc", + "patient_data", + "reserve_ac", + "role", + "user", + "vocation", + "unknown", + "ALL", + "*" + ], + "type": "string", + "description": "Module filter; single/comma-separated values or ALL/*", + "name": "filter[module]", + "in": "query" + }, + { + "enum": [ + "air_rescuer_checklist", + "audit_log", + "auth", + "base", + "base_regular", + "base_hems", + "base_roster", + "branding", + "contact", + "duty_roster", + "facility", + "federal_state", + "file_manager", + "flight", + "flight_data", + "forces_present", + "health_insurance_company", + "helicopter", + "helicopter_file", + "hems_roster", + "hospital", + "icao", + "insurance_patient_data", + "land", + "master_setting", + "medicine", + "mission", + "opc", + "patient_data", + "reserve_ac", + "role", + "user", + "vocation", + "unknown", + "ALL", + "*" + ], + "type": "string", + "description": "Alias for filter[module]", + "name": "module", + "in": "query" + }, + { + "enum": [ + "list", + "get", + "create", + "update", + "delete", + "export", + "custom", + "ALL", + "*" + ], + "type": "string", + "description": "Action filter; single/comma-separated values or ALL/*", + "name": "filter[action]", + "in": "query" + }, + { + "enum": [ + "list", + "get", + "create", + "update", + "delete", + "export", + "custom", + "ALL", + "*" + ], + "type": "string", + "description": "Alias for filter[action]", + "name": "action", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 200)", + "name": "page[size]", + "in": "query" + }, + { + "enum": [ + "created_at", + "-created_at", + "module", + "-module", + "action", + "-action", + "layer", + "-layer", + "status_code", + "-status_code", + "success", + "-success" + ], + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/email-otp/send": { + "post": { + "description": "Sends a one-time 6-digit OTP to user email using login challenge token from /auth/login.\nSecurity controls: old OTP is invalidated, resend cooldown is enforced, and resend/hour quota is enforced.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Email OTP" + ], + "summary": "Send login OTP to email", + "parameters": [ + { + "description": "JSON:API Email OTP send request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPSendRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPSendResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/email-otp/verify": { + "post": { + "description": "Verifies one-time OTP and completes login by issuing auth cookies.\nOTP is one-time use, expires automatically, and failed attempts are limited and audited.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Email OTP" + ], + "summary": "Verify login OTP from email", + "parameters": [ + { + "description": "JSON:API Email OTP verify request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPVerifyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPVerifyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/exchange": { + "post": { + "description": "Accepts Microsoft token payload from frontend ssoSilent and issues backend auth cookies.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Exchange frontend Microsoft SSO token into backend session", + "parameters": [ + { + "description": "JSON:API Exchange request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ExchangeRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TokenResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/forgot-password": { + "post": { + "description": "Always returns a generic success response.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Forgot Password" + ], + "summary": "Forgot password", + "parameters": [ + { + "description": "JSON:API Forgot password request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForgotPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/invite": { + "post": { + "description": "Creates user without password and sends set-password link to email.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Invite user", + "parameters": [ + { + "description": "JSON:API Invite request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InviteRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/invite/resend-set-password": { + "post": { + "description": "Triggers a new set-password invite email for an invited user by user_id. Response is generic for security.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Resend set-password invite", + "parameters": [ + { + "description": "JSON:API resend set-password invite request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResendSetPasswordInviteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/login": { + "post": { + "description": "If TOTP enabled, returns 202 + challenge_token (use /auth/totp/verify).\nIf TOTP is not enabled, returns 202 + email OTP challenge_token (use /auth/email-otp/send then /auth/email-otp/verify).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Login with email \u0026 password", + "parameters": [ + { + "description": "JSON:API Login request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthCurrentUserResponse" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LoginEmailOTPRequiredResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/logout": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Refresh" + ], + "summary": "Logout (revoke refresh)", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VerifyEmailResponse" + } + } + } + } + }, + "/api/v1/auth/me": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Get current user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthCurrentUserResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/me/timezone": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Update current user timezone", + "parameters": [ + { + "description": "JSON:API timezone update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TimezoneUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthCurrentUserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/callback": { + "get": { + "description": "Completes Microsoft SSO login. Login succeeds only when SSO identity is already linked to an existing user.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Microsoft Entra SSO callback", + "parameters": [ + { + "type": "string", + "description": "Authorization code", + "name": "code", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "State", + "name": "state", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MicrosoftCallbackResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/front-channel-logout": { + "get": { + "description": "OIDC front-channel logout endpoint. Configure this URL as the \"Front-channel logout URL\" in Microsoft Entra. Microsoft loads it in a hidden iframe when the user signs out at the IdP (or from another RP). It clears local auth cookies and revokes the server-side session plus the stored Microsoft refresh token for the browser that owns the request. It never redirects and always returns 200 so the IdP logout flow can complete.", + "produces": [ + "text/html" + ], + "tags": [ + "Auth - SSO" + ], + "summary": "Microsoft front-channel logout callback", + "responses": { + "200": { + "description": "logged out", + "schema": { + "type": "string" + } + } + } + } + }, + "/api/v1/auth/microsoft/link": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns JSON:API payload containing Microsoft authorization URL for linking SSO to the current authenticated user.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Microsoft Entra SSO link URL", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MicrosoftLoginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/login": { + "get": { + "description": "Returns JSON:API payload containing Microsoft authorization URL.\nBackend starts with silent session-check (` + "`" + `prompt=none` + "`" + `). If Microsoft session is inactive, callback automatically falls back to interactive login.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Microsoft Entra SSO login", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MicrosoftLoginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/login-check": { + "get": { + "description": "Returns JSON:API payload containing Microsoft authorization URL with prompt=none for strict session check.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Microsoft Entra silent session check URL", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MicrosoftLoginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/logout": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth - SSO" + ], + "summary": "Logout Microsoft OAuth session", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VerifyEmailResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/reauth": { + "get": { + "description": "Starts a Microsoft reauthentication flow for supported sensitive actions.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Microsoft Entra SSO reauth URL", + "parameters": [ + { + "type": "string", + "description": "Supported values: pin_reset_request, pin_reset_confirm", + "name": "purpose", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Required when purpose=pin_reset_confirm", + "name": "token", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MicrosoftLoginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/microsoft/refresh": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth - SSO" + ], + "summary": "Refresh Microsoft access token", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TokenResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "428": { + "description": "Precondition Required", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/pin/change": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - PIN" + ], + "summary": "Change security PIN", + "parameters": [ + { + "description": "JSON:API security PIN change request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINChangeRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionsResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/pin/forgot": { + "post": { + "description": "Always returns a generic success response.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - PIN" + ], + "summary": "Forgot security PIN", + "parameters": [ + { + "description": "JSON:API forgot security PIN request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForgotSecurityPINRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/pin/request-reset": { + "post": { + "description": "Verifies current account password, then sends PIN reset email to currently logged-in user when PIN is blocked.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - PIN" + ], + "summary": "Request security PIN reset email for blocked PIN", + "parameters": [ + { + "description": "JSON:API request security PIN reset payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RequestSecurityPINRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RequestSecurityPINResetResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/pin/reset": { + "post": { + "description": "Resets security PIN using a re-auth method. For method=password, send the single-use reset token from the email link plus password. For method=microsoft, send only reauth_token from Microsoft re-auth.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - PIN" + ], + "summary": "Reset security PIN", + "parameters": [ + { + "description": "JSON:API reset security PIN request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResetSecurityPINRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/pin/setup": { + "post": { + "description": "Creates a 6-digit security PIN for sensitive actions.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - PIN" + ], + "summary": "Setup security PIN", + "parameters": [ + { + "description": "JSON:API security PIN setup request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINSetupRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINSetupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/pin/verify": { + "post": { + "description": "Verifies PIN and returns a short-lived one-time action token.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - PIN" + ], + "summary": "Verify security PIN for sensitive action", + "parameters": [ + { + "description": "JSON:API security PIN verify request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINVerifyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINVerifyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/refresh": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Refresh" + ], + "summary": "Refresh access token", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TokenResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/register": { + "post": { + "description": "Creates user (public role: from AUTH_DEFAULT_ROLE, fallback: user) and sends verification link to email without issuing an auth session.\nNext: open verify link -\u003e then login.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Register user with email \u0026 password", + "parameters": [ + { + "description": "JSON:API Register request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RegisterRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/reset-password": { + "post": { + "description": "Resets password using a single-use reset token.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Forgot Password" + ], + "summary": "Reset password", + "parameters": [ + { + "description": "JSON:API Reset password request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResetPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/sessions": { + "get": { + "description": "Lists all active sessions/devices for current user.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Refresh" + ], + "summary": "List active sessions", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionsResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/sessions/{session_id}": { + "delete": { + "description": "Revokes one session/device immediately. Requires PIN action token (header X-PIN-Verification-Token) with action=3d7d1d5f-ec48-4eca-a7ea-37f4f7927d06.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth - Refresh" + ], + "summary": "Delete one session", + "parameters": [ + { + "type": "string", + "description": "Session ID", + "name": "session_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionDeleteResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/set-password": { + "post": { + "description": "Sets first password for invited user and marks email as verified.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Set password from invite token", + "parameters": [ + { + "description": "JSON:API Set password request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SetPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnResetSetupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/sso/link": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Link Microsoft SSO identity to the authenticated user. SSO login will only work after successful link.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Link Microsoft SSO to current user", + "parameters": [ + { + "description": "JSON:API SSO link request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOLinkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOLinkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/sso/unlink": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove Microsoft SSO mapping from authenticated user.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Unlink Microsoft SSO from current user", + "parameters": [ + { + "description": "JSON:API SSO unlink request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOUnlinkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOUnlinkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/totp/confirm": { + "post": { + "description": "Validates initial TOTP code and enables TOTP for future logins.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - TOTP" + ], + "summary": "Confirm TOTP setup", + "parameters": [ + { + "description": "JSON:API TOTP confirm request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPConfirmRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VerifyEmailResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/totp/disable": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - TOTP" + ], + "summary": "Disable TOTP", + "parameters": [ + { + "description": "JSON:API TOTP disable request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPDisableRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VerifyEmailResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/totp/reset-setup": { + "post": { + "description": "Removes TOTP setup so the user can re-setup authenticator.\nFor method=pin, obtain token from /auth/pin/verify with action=0b5409b5-94f3-4aab-9de2-44b0ad5858f1 and send it in X-PIN-Verification-Token header.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - TOTP" + ], + "summary": "Reset TOTP setup", + "parameters": [ + { + "description": "JSON:API TOTP reset setup request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPResetSetupRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VerifyEmailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/totp/setup": { + "post": { + "description": "Generates secret and otpauth URL. TOTP is NOT enabled until confirmed.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - TOTP" + ], + "summary": "Setup TOTP", + "parameters": [ + { + "description": "JSON:API TOTP setup request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPSetupRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPSetupResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/totp/verify": { + "post": { + "description": "Completes login when TOTP is enabled and returns auth cookies.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - TOTP" + ], + "summary": "Verify TOTP code", + "parameters": [ + { + "description": "JSON:API TOTP verify request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPVerifyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TokenResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/verify-email": { + "get": { + "description": "Confirms email verification after register.", + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Verify email", + "parameters": [ + { + "type": "string", + "description": "Verification token", + "name": "token", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/webauthn/login/begin": { + "post": { + "description": "Creates WebAuthn assertion options for a user email.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - WebAuthn" + ], + "summary": "Begin WebAuthn login", + "parameters": [ + { + "description": "JSON:API WebAuthn login begin request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnLoginBeginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnBeginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/webauthn/login/finish": { + "post": { + "description": "Verifies WebAuthn assertion and issues auth cookies.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - WebAuthn" + ], + "summary": "Finish WebAuthn login", + "parameters": [ + { + "description": "JSON:API WebAuthn login finish request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnLoginFinishRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TokenResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/webauthn/register/begin": { + "post": { + "description": "Creates WebAuthn registration options for the authenticated user.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - WebAuthn" + ], + "summary": "Begin WebAuthn registration", + "parameters": [ + { + "description": "JSON:API WebAuthn register begin request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnRegisterBeginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnBeginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/webauthn/register/finish": { + "post": { + "description": "Verifies WebAuthn registration response and stores the credential.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - WebAuthn" + ], + "summary": "Finish WebAuthn registration", + "parameters": [ + { + "description": "JSON:API WebAuthn register finish request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnRegisterFinishRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/webauthn/reset-setup": { + "post": { + "description": "Removes all registered WebAuthn credentials so the user can re-setup passkeys.\nFor method=pin, obtain token from /auth/pin/verify with action=1f7d457e-0a19-4a36-8d9f-2c66fd2369ab and send it in X-PIN-Verification-Token header.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth - WebAuthn" + ], + "summary": "Reset WebAuthn setup", + "parameters": [ + { + "description": "JSON:API WebAuthn reset setup request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnResetSetupRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "429": { + "description": "Too Many Requests", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/bases/create": { + "post": { + "description": "Example request:\n{\"data\":{\"type\":\"base\",\"attributes\":{\"base\":\"Lude Testing 2\",\"base_abbreviation\":\"LOIG\",\"base_category\":\"regular\",\"address\":\"Lude Airfield\",\"latitude\":47.3769,\"longitude\":8.5417,\"sortkey\":1,\"sms_alert\":false,\"checklist\":false,\"leg_time\":\"00:20\",\"default_start_time_type\":\"FIXED\",\"default_end_time_type\":\"FIXED\",\"default_shift_start\":\"06:00\",\"default_shift_end\":\"21:00\",\"utc\":\"0\",\"notes\":\"Main base\",\"operational_shift_times\":[{\"date_start\":\"2026-06-15\",\"date_end\":\"2026-06-15\",\"start_time_type\":\"BMCT\",\"end_time_type\":\"ECET\"}]}}}", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Bases" + ], + "summary": "Create base", + "parameters": [ + { + "description": "JSON:API base create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/bases/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Bases" + ], + "summary": "Delete base", + "parameters": [ + { + "type": "string", + "description": "Base UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/bases/get-all": { + "get": { + "description": "JSON:API list with filtering and sorting. This endpoint returns all bases.", + "produces": [ + "application/json" + ], + "tags": [ + "Bases" + ], + "summary": "List bases", + "parameters": [ + { + "enum": [ + "regular", + "hems" + ], + "type": "string", + "description": "Base category", + "name": "category_type", + "in": "query" + }, + { + "enum": [ + "regular", + "hems" + ], + "type": "string", + "description": "Alias filter for base category", + "name": "base_category", + "in": "query" + }, + { + "type": "string", + "description": "Search by base/base_abbreviation/address/email/phone", + "name": "search", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by DUL flag (true/false)", + "name": "dul", + "in": "query" + }, + { + "type": "integer", + "description": "Ignored", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Ignored", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Ignored", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseListResponse" + } + } + } + } + }, + "/api/v1/bases/get-all/dt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Bases" + ], + "summary": "List bases (datatable)", + "parameters": [ + { + "enum": [ + "regular", + "hems" + ], + "type": "string", + "description": "Base category", + "name": "category_type", + "in": "query" + }, + { + "enum": [ + "regular", + "hems" + ], + "type": "string", + "description": "Alias filter for base category", + "name": "base_category", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by DUL flag (true/false)", + "name": "dul", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseDataTableResponse" + } + } + } + } + }, + "/api/v1/bases/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Bases" + ], + "summary": "Get base by ID", + "parameters": [ + { + "type": "string", + "description": "Base UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/bases/update/{uuid}": { + "patch": { + "description": "Example request:\n{\"data\":{\"type\":\"hems_base\",\"id\":\"\u003cuuid\u003e\",\"attributes\":{\"base\":\"Lude Testing 2\",\"base_abbreviation\":\"LOIG\",\"base_category\":\"regular\",\"address\":\"Lude Airfield\",\"latitude\":47.3769,\"longitude\":8.5417,\"sortkey\":1,\"sms_alert\":false,\"checklist\":false,\"leg_time\":\"00:20\",\"default_start_time_type\":\"FIXED\",\"default_end_time_type\":\"FIXED\",\"default_shift_start\":\"06:00\",\"default_shift_end\":\"21:00\",\"utc\":\"0\",\"notes\":\"Main base\",\"operational_shift_times\":[{\"date_start\":\"2026-06-15\",\"date_end\":\"2026-06-15\",\"start_time_type\":\"BMCT\",\"end_time_type\":\"ECET\"}]}}}", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Bases" + ], + "summary": "Update base", + "parameters": [ + { + "type": "string", + "description": "Base UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API base update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/branding/login-content": { + "get": { + "description": "Return public stable login title and subtitle from active master settings with backend defaults.\nSupports conditional requests with ` + "`" + `If-None-Match` + "`" + ` / ` + "`" + `If-Modified-Since` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Branding" + ], + "summary": "Get login content", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/internal_transport_http_handlers.loginBrandingContentDocument" + } + }, + "304": { + "description": "Not Modified", + "schema": { + "type": "string" + } + } + } + } + }, + "/api/v1/branding/login-cover": { + "get": { + "description": "Return public stable login cover image for sign-in page.\nSupports conditional requests with ` + "`" + `If-None-Match` + "`" + ` / ` + "`" + `If-Modified-Since` + "`" + `.", + "produces": [ + "image/png", + "image/jpeg", + "image/webp", + "image/svg+xml", + "application/octet-stream" + ], + "tags": [ + "Branding" + ], + "summary": "Get login cover image", + "responses": { + "200": { + "description": "Login cover image binary", + "schema": { + "type": "file" + } + }, + "304": { + "description": "Not Modified", + "schema": { + "type": "string" + } + } + } + } + }, + "/api/v1/branding/logo": { + "get": { + "description": "Return a stable company logo asset for frontend consumption.\nBackend resolves private object storage internally (no presigned URL exposure) and serves fallback logo when managed logo is unavailable.\nSupports conditional requests with ` + "`" + `If-None-Match` + "`" + ` / ` + "`" + `If-Modified-Since` + "`" + `.", + "produces": [ + "image/png", + "image/jpeg", + "image/webp", + "image/svg+xml", + "application/octet-stream" + ], + "tags": [ + "Branding" + ], + "summary": "Get branding logo", + "responses": { + "200": { + "description": "Branding logo binary", + "schema": { + "type": "file" + } + }, + "304": { + "description": "Not Modified", + "schema": { + "type": "string" + } + } + } + } + }, + "/api/v1/complaints/action-taken/create/{id}": { + "post": { + "description": "Records the corrective action text taken for a complaint. Recording the action does NOT sign it and does NOT lift the AOG grounding: signing the action is a separate step at POST /api/v1/action-signoffs/sign/{helicopter_id} (send complaint_id), and returning the aircraft to service requires a formal release (a signed EASA release / CRS, or an NSR). The complaint stays open until an EASA release fixes it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Record the corrective action for a complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Action taken payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintActionTakenRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/action-taken/update/{id}": { + "patch": { + "description": "Updates the previously-recorded corrective action text of a complaint. Recording/updating the action does NOT sign it — signing is a separate step at POST /api/v1/action-signoffs/sign/{helicopter_id} (send complaint_id).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Update the recorded corrective action for a complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Action taken payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintActionTakenRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/create": { + "post": { + "description": "Create a complaint (defect) for a flight inspection.\nMEL severity (mel_severity, OPTIONAL): omit it to file a \"pending_mel\" complaint and classify later (via PATCH /complaints/update/{id}); it MUST be classified before recording action taken. A pending complaint does NOT ground the aircraft — grounding starts at classification. Values: 0 = NON-MEL (grounds immediately); 1 = A (1 day grace); 2 = B (3 days); 3 = C (10 days); 4 = D (120 days). After the grace period passes without action the aircraft becomes AOG.\nNSR (Non-Safety Release) is its own flow — sign it at POST /api/v1/complaints/nsr/sign/{id} (cancel at /nsr/unsign/{id}); it is no longer set via create/update.\nOptional fields: aircraft_hours_at_report.\nTo record the corrective action, use POST /api/v1/complaints/action-taken/create/{id} (a separate, explicitly-signed step) — it is no longer part of create/update.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Create complaint", + "parameters": [ + { + "description": "JSON:API complaint create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/delete/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Delete complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/get-all": { + "get": { + "description": "Lists complaints grouped by helicopter. Filter by date range on the reported date, by the name of an involved person (reporter / MEL classifier / action signer / NSR decider — i.e. pilot/techniker), and by free-text search on the complaint description.", + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "List complaints", + "parameters": [ + { + "type": "string", + "description": "Search complaint description", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Reported date lower bound (YYYY-MM-DD)", + "name": "date_from", + "in": "query" + }, + { + "type": "string", + "description": "Reported date upper bound (YYYY-MM-DD)", + "name": "date_to", + "in": "query" + }, + { + "type": "string", + "description": "Involved person name (pilot/techniker, partial match)", + "name": "person", + "in": "query" + }, + { + "type": "string", + "description": "Filter by helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "query" + }, + { + "type": "string", + "description": "Sort complaints", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Page size", + "name": "page_size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintGroupedListResponse" + } + } + } + } + }, + "/api/v1/complaints/get-all/dt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "List complaints grouped by helicopter (datatable)", + "parameters": [ + { + "type": "string", + "description": "Search complaint description", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Reported date lower bound (YYYY-MM-DD)", + "name": "date_from", + "in": "query" + }, + { + "type": "string", + "description": "Reported date upper bound (YYYY-MM-DD)", + "name": "date_to", + "in": "query" + }, + { + "type": "string", + "description": "Involved person name (pilot/techniker, partial match)", + "name": "person", + "in": "query" + }, + { + "type": "string", + "description": "Filter by helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "query" + }, + { + "type": "string", + "description": "Sort complaints", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "default": 1, + "description": "Datatable draw counter", + "name": "draw", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Datatable start offset", + "name": "start", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Datatable page length", + "name": "length", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintGroupedDataTableResponse" + } + } + } + } + }, + "/api/v1/complaints/get-by-helicopter/{helicopter_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "List complaints by helicopter", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID", + "name": "helicopter_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintGroupedListResponse" + } + } + } + } + }, + "/api/v1/complaints/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Get complaint by ID", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/hold-items/get-all": { + "get": { + "description": "Returns the Hold Item List (HIL): complaints that are still OPEN (not yet fixed by an EASA release) AND MEL-deferred (mel_severity \u003e 0). Non-MEL defects (which ground the aircraft immediately) and already-serviced complaints are excluded. Same grouped shape and filters as get-all.", + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "List hold items (open MEL-deferred defects)", + "parameters": [ + { + "type": "string", + "description": "From date — reported_at \u003e= (YYYY-MM-DD). Legacy 'Von'.", + "name": "date_from", + "in": "query" + }, + { + "type": "string", + "description": "To date — reported_at \u003c= (YYYY-MM-DD). Legacy 'bis'.", + "name": "date_to", + "in": "query" + }, + { + "type": "string", + "description": "Pilot/Technician name — matches reporter or action signer. Legacy 'Pilot/Techniker'.", + "name": "person", + "in": "query" + }, + { + "type": "string", + "description": "Search complaint description / MEL. Legacy 'Complains'.", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20)", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "Sort expression (e.g. reported_at DESC)", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintGroupedListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/hold-items/get-all/dt": { + "get": { + "description": "Datatable variant of the Hold Item List (open + MEL-deferred complaints). Same filters as /hold-items/get-all (date_from, date_to, person, search, helicopter_id) plus DataTables params (start, length, draw).", + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "List hold items (open MEL-deferred defects) — datatable", + "parameters": [ + { + "type": "string", + "description": "From date — reported_at \u003e= (YYYY-MM-DD). Legacy 'Von'.", + "name": "date_from", + "in": "query" + }, + { + "type": "string", + "description": "To date — reported_at \u003c= (YYYY-MM-DD). Legacy 'bis'.", + "name": "date_to", + "in": "query" + }, + { + "type": "string", + "description": "Pilot/Technician name — matches reporter or action signer.", + "name": "person", + "in": "query" + }, + { + "type": "string", + "description": "Search complaint description / MEL.", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "query" + }, + { + "type": "integer", + "description": "DataTables start offset", + "name": "start", + "in": "query" + }, + { + "type": "integer", + "description": "DataTables page length", + "name": "length", + "in": "query" + }, + { + "type": "integer", + "description": "DataTables draw counter", + "name": "draw", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintGroupedListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/nsr/sign/{id}": { + "post": { + "description": "Records an NSR decision on its own flow (separate from complaint update): the aircraft is released to fly despite the open defect and is no longer grounded by it. MEL must be classified first — an unassessed (pending_mel) defect cannot be deferred, so a pending complaint returns 422. The complaint stays OPEN and is only closed by a signed EASA release. Requires the complaint.nsr permission.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Sign a Non-Safety Release (NSR / MEL deferral) for a complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Optional NSR reason", + "name": "request", + "in": "body", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintNSRSignRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/nsr/unsign/{id}": { + "post": { + "description": "Revokes the NSR decision: the deferral is lifted, so the still-open defect grounds the aircraft again (AOG is recomputed). Requires the complaint.nsr permission.", + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Cancel a Non-Safety Release for a complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/complaints/update/{id}": { + "patch": { + "description": "Update a complaint. All attributes optional; only provided fields change.\nmel_severity: 0 = NON-MEL (immediate AOG); 1 = A (1 day); 2 = B (3 days); 3 = C (10 days); 4 = D (120 days) — AOG after grace passes.\nNSR is not set here — it is its own flow: POST /api/v1/complaints/nsr/sign/{id} (and /nsr/unsign/{id}).\nNote: the corrective action is recorded via POST /api/v1/complaints/action-taken/create/{id} (a separate, signed step), not here.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Complaints" + ], + "summary": "Update complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API complaint update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/contact/other": { + "get": { + "description": "Lists master other-people whose name/mobile_phone/email matches ` + "`" + `q` + "`" + ` (empty ` + "`" + `q` + "`" + ` = most recent). Pick one and pass its id as ` + "`" + `other_person_id` + "`" + ` in a takeover's other_person to reuse without re-entering the data.", + "produces": [ + "application/json" + ], + "tags": [ + "Contact - Other People" + ], + "summary": "Search reusable \"other people\" (non-staff) for the takeover picker", + "parameters": [ + { + "type": "string", + "description": "Search text (name / mobile_phone / email)", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "Max results (default 20)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Offset", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonListResponse" + } + } + } + } + }, + "/api/v1/contact/other/create": { + "post": { + "description": "Registers a person in the other-people master so it can be reused across takeovers. The (name, mobile_phone, email) identity must be unique.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contact - Other People" + ], + "summary": "Create a reusable \"other person\" (non-staff)", + "parameters": [ + { + "description": "Other person", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.OtherPersonRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonResponse" + } + } + } + } + }, + "/api/v1/contact/other/delete/{id}": { + "delete": { + "description": "Soft-deletes a master person. Existing takeover snapshots keep their own copy of the data.", + "produces": [ + "application/json" + ], + "tags": [ + "Contact - Other People" + ], + "summary": "Delete a reusable \"other person\"", + "parameters": [ + { + "type": "string", + "description": "Other person id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/api/v1/contact/other/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Contact - Other People" + ], + "summary": "Get one reusable \"other person\"", + "parameters": [ + { + "type": "string", + "description": "Other person id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonResponse" + } + } + } + } + }, + "/api/v1/contact/other/update/{id}": { + "patch": { + "description": "Replaces the name/mobile_phone/email of a master person. The new identity must not collide with another person.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contact - Other People" + ], + "summary": "Update a reusable \"other person\"", + "parameters": [ + { + "type": "string", + "description": "Other person id", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Other person", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.OtherPersonRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonResponse" + } + } + } + } + }, + "/api/v1/contacts/bulk-update": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Bulk update contacts status", + "parameters": [ + { + "description": "JSON:API bulk update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactBulkUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactSimpleSuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/create": { + "post": { + "description": "Create new contact user and role-specific profile.\n` + "`" + `data.type` + "`" + ` must be ` + "`" + `contact_create` + "`" + `.\n` + "`" + `role_ids` + "`" + ` determines roles (use ` + "`" + `role_id` + "`" + ` only for backward compatibility). Every selected role must include its matching role-specific profile object in ` + "`" + `attributes` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Create contact", + "parameters": [ + { + "description": "JSON:API Contact create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/get-all": { + "get": { + "description": "Filter contacts by role and pilot_category. Sort values: name, -name, first_name, -first_name, last_name, -last_name, email, -email, sortkey, -sortkey, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "List contacts", + "parameters": [ + { + "type": "string", + "description": "Role: pilot|doctor|air_rescuer|technician|flight_assistant|staff", + "name": "role", + "in": "query" + }, + { + "type": "string", + "description": "Pilot category: regular|freelance|dry_lease", + "name": "pilot_category", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter doctor chief flag", + "name": "chief_doctor", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter technician chief flag", + "name": "chief_technician", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter responsible complaint flag", + "name": "responsible_complaint", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter pilot chief flag", + "name": "chief_pilot", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter IFR qualified flag", + "name": "ifr_qualified", + "in": "query" + }, + { + "type": "string", + "description": "Search by username/email/name", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Limit (default 20)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Offset (default 0)", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/get-all/dt": { + "get": { + "description": "Datatable response with pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "List contacts (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 1000)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Role filter", + "name": "role", + "in": "query" + }, + { + "type": "string", + "description": "Pilot category filter", + "name": "pilot_category", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter doctor chief flag", + "name": "chief_doctor", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter technician chief flag", + "name": "chief_technician", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter responsible complaint flag", + "name": "responsible_complaint", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter pilot chief flag", + "name": "chief_pilot", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter IFR qualified flag", + "name": "ifr_qualified", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/{user_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Get contact", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Delete contact", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + }, + "patch": { + "description": "` + "`" + `data.type` + "`" + ` must be ` + "`" + `contact_update` + "`" + `.\nSupports updating ` + "`" + `role_id` + "`" + ` and ` + "`" + `role_ids` + "`" + ` (multiple roles) in the same request. Sending ` + "`" + `role_ids: []` + "`" + ` clears all assigned roles.\nOnly one role-specific profile object is allowed in ` + "`" + `attributes` + "`" + `. If provided, it must match one of the contact's assigned roles.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Update contact", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API Contact update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/{user_id}/chief-flags": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Update chief flags", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API chief flags request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactChiefFlagsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/{user_id}/license": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Update contact license fields", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API license update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactLicenseUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/{user_id}/pilot-category": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Update pilot category", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API pilot category request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactPilotCategoryUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/{user_id}/role": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Update contact role", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API role update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactRoleUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/contacts/{user_id}/status": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contacts" + ], + "summary": "Update contact active status", + "parameters": [ + { + "type": "string", + "description": "User UUID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API status update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactStatusUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactSimpleSuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/dul/create": { + "post": { + "description": "Possible error code: DUL_INVALID_JSON, DUL_INVALID_UUID, DUL_DATE_INVALID, DUL_VALIDATION_FAILED, DUL_CREATE_FAILED.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DUL" + ], + "summary": "Create DUL", + "parameters": [ + { + "description": "DUL create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/dul/delete/{id}": { + "delete": { + "description": "Possible error code: DUL_INVALID_UUID, DUL_DELETE_FAILED.", + "produces": [ + "application/json" + ], + "tags": [ + "DUL" + ], + "summary": "Delete DUL", + "parameters": [ + { + "type": "string", + "description": "DUL ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/dul/get-all": { + "get": { + "description": "Possible error code: DUL_LIST_FAILED.", + "produces": [ + "application/json" + ], + "tags": [ + "DUL" + ], + "summary": "List DUL grouped by base", + "parameters": [ + { + "type": "string", + "description": "Search by name/info", + "name": "search", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number alias", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size alias", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size alias", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/dul/get-all/dt": { + "get": { + "description": "Possible error code: DUL_LIST_FAILED.", + "produces": [ + "application/json" + ], + "tags": [ + "DUL" + ], + "summary": "List DUL grouped by base (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search by name/info", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/dul/get/{id}": { + "get": { + "description": "Possible error code: DUL_INVALID_UUID, DUL_NOT_FOUND.", + "produces": [ + "application/json" + ], + "tags": [ + "DUL" + ], + "summary": "Get DUL by ID", + "parameters": [ + { + "type": "string", + "description": "DUL ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/dul/update/{id}": { + "patch": { + "description": "Possible error code: DUL_INVALID_JSON, DUL_INVALID_UUID, DUL_DATE_INVALID, DUL_VALIDATION_FAILED, DUL_NOT_FOUND, DUL_UPDATE_FAILED.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DUL" + ], + "summary": "Update DUL", + "parameters": [ + { + "type": "string", + "description": "DUL ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "DUL update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/duty-roster/create": { + "post": { + "description": "Unified endpoint for all base categories.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Duty Roster" + ], + "summary": "Create duty roster", + "parameters": [ + { + "description": "Create request payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/duty-roster/crew": { + "delete": { + "description": "Unified crew delete endpoint by ` + "`" + `data.id` + "`" + ` and ` + "`" + `roster_detail` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Duty Roster" + ], + "summary": "Remove duty roster crew assignment", + "parameters": [ + { + "description": "Crew delete request payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewByIDRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrewDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/duty-roster/delete": { + "delete": { + "description": "Unified delete endpoint by ` + "`" + `data.id` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Duty Roster" + ], + "summary": "Delete duty roster", + "parameters": [ + { + "description": "Delete request payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterAnyDeleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/duty-roster/get-all": { + "get": { + "description": "Combined roster list across HEMS and Base for date range and optional base filters.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Duty Roster" + ], + "summary": "List all rosters (HEMS + Base)", + "parameters": [ + { + "type": "string", + "description": "HEMS base UUID", + "name": "hems_base_id", + "in": "query" + }, + { + "type": "string", + "description": "Base UUID", + "name": "base_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by base name (contains, case-insensitive)", + "name": "base_name", + "in": "query" + }, + { + "type": "string", + "description": "Start date (YYYY-MM-DD)", + "name": "from", + "in": "query" + }, + { + "type": "string", + "description": "End date (YYYY-MM-DD)", + "name": "to", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/duty-roster/get-all/dt": { + "get": { + "description": "Unified datatable endpoint. Optional ` + "`" + `base_type` + "`" + ` filter: ` + "`" + `hems` + "`" + ` or ` + "`" + `base` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Duty Roster" + ], + "summary": "List duty roster (datatable)", + "parameters": [ + { + "type": "string", + "description": "Base type filter (hems|base)", + "name": "base_type", + "in": "query" + }, + { + "type": "string", + "description": "Base UUID", + "name": "base_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by base name", + "name": "base_name", + "in": "query" + }, + { + "type": "string", + "description": "Start date (YYYY-MM-DD)", + "name": "from", + "in": "query" + }, + { + "type": "string", + "description": "End date (YYYY-MM-DD)", + "name": "to", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter", + "name": "draw", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterGroupedDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/duty-roster/update": { + "patch": { + "description": "Unified update endpoint by ` + "`" + `data.id` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Duty Roster" + ], + "summary": "Update duty roster (partial)", + "parameters": [ + { + "description": "Update request payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/easa-releases/create": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Create EASA maintenance release (draft) for a helicopter", + "parameters": [ + { + "description": "JSON:API EASA release create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.EASAReleaseCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResponse" + } + } + } + } + }, + "/api/v1/easa-releases/delete/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Delete (void) an EASA release", + "parameters": [ + { + "type": "string", + "description": "EASA release ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseDeleteResponse" + } + } + } + } + }, + "/api/v1/easa-releases/get-by-complaint/{complaint_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Get the current EASA release for a complaint", + "parameters": [ + { + "type": "string", + "description": "Complaint ID (UUIDv7)", + "name": "complaint_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResponse" + } + } + } + } + }, + "/api/v1/easa-releases/get-by-flight/{flight_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Get the current EASA release for a flight", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResponse" + } + } + } + } + }, + "/api/v1/easa-releases/get-by-helicopter/{helicopter_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "List EASA releases for a helicopter", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseListResponse" + } + } + } + } + }, + "/api/v1/easa-releases/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Get EASA release by ID", + "parameters": [ + { + "type": "string", + "description": "EASA release ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResponse" + } + } + } + } + }, + "/api/v1/easa-releases/sign/{id}": { + "post": { + "description": "Signs the release and closes the helicopter's open complaints. The corrective-action sign-off is re-checked at sign time: if the sign-off was invalidated since the draft was created (e.g. its complaint was reopened) it returns 422 ` + "`" + `EASA_RELEASE_SIGNOFF_REQUIRED` + "`" + ` and does not sign.", + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Sign the EASA maintenance release (closes the helicopter's open complaints)", + "parameters": [ + { + "type": "string", + "description": "EASA release ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/easa-releases/update/{id}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "EASA Releases" + ], + "summary": "Update EASA release (draft fields)", + "parameters": [ + { + "type": "string", + "description": "EASA release ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API EASA release update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.EASAReleaseUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResponse" + } + } + } + } + }, + "/api/v1/facilities/create": { + "post": { + "description": "Create a facility row using one shared table. Flight-data SPO references now expect categories like ` + "`" + `heslo-rope-label` + "`" + `, ` + "`" + `heslo-rope-length` + "`" + `, ` + "`" + `heslo-hook` + "`" + `, ` + "`" + `hec-rope-label` + "`" + `, and ` + "`" + `hec-rope-length` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Facilities" + ], + "summary": "Create facility", + "parameters": [ + { + "description": "JSON:API Facility create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/facilities/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Facilities" + ], + "summary": "Delete facility", + "parameters": [ + { + "type": "string", + "description": "Facility UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/facilities/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Supports one shared table with category discriminator (e.g. ` + "`" + `heslo-rope-label` + "`" + `, ` + "`" + `heslo-hook` + "`" + `, ` + "`" + `hec-rope-label` + "`" + `).", + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Facilities" + ], + "summary": "List facilities", + "parameters": [ + { + "type": "string", + "description": "Search by name/type/length/weight", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Filter by category (e.g. heslo-rope-label, heslo-hook, hec-rope-label)", + "name": "category", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityListResponse" + } + } + } + } + }, + "/api/v1/facilities/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Facilities" + ], + "summary": "List facilities (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Facility category", + "name": "category", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityDataTableResponse" + } + } + } + } + }, + "/api/v1/facilities/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Facilities" + ], + "summary": "Get facility by ID", + "parameters": [ + { + "type": "string", + "description": "Facility UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/facilities/update/{uuid}": { + "patch": { + "description": "Patch facility by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Facilities" + ], + "summary": "Update facility (partial)", + "parameters": [ + { + "type": "string", + "description": "Facility UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API Facility update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/federal-state/create": { + "post": { + "description": "Create a federal state row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Federal State" + ], + "summary": "Create federal state", + "parameters": [ + { + "description": "JSON:API federal state create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/federal-state/delete/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Federal State" + ], + "summary": "Delete federal state", + "parameters": [ + { + "type": "string", + "description": "Federal State ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/federal-state/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, sortkey, -sortkey, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Federal State" + ], + "summary": "List federal state", + "parameters": [ + { + "type": "string", + "description": "Search by name", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter federal state by land UUID", + "name": "land_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter federal state by land name", + "name": "land_name", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateListResponse" + } + } + } + } + }, + "/api/v1/federal-state/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Federal State" + ], + "summary": "List federal state (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search by name", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter federal state by land UUID", + "name": "land_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter federal state by land name", + "name": "land_name", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateDataTableResponse" + } + } + } + } + }, + "/api/v1/federal-state/update/{id}": { + "patch": { + "description": "Patch federal state by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Federal State" + ], + "summary": "Update federal state (partial)", + "parameters": [ + { + "type": "string", + "description": "Federal State ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API federal state update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/attachments/create": { + "post": { + "description": "Create generic attachment relation between a file and business reference (ref_type/ref_id).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Attach file to business reference", + "operationId": "FileManagerCreateAttachment", + "parameters": [ + { + "description": "JSON:API attachment create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerAttachmentCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerAttachmentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/attachments/delete/{uuid}": { + "delete": { + "description": "Remove generic attachment relation by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Detach attachment relation", + "operationId": "FileManagerDeleteAttachment", + "parameters": [ + { + "type": "string", + "description": "Attachment UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/attachments/get-all": { + "get": { + "description": "List generic attachment relations by ref_type and ref_id.", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "List attachments by business reference", + "operationId": "FileManagerListAttachmentsByReference", + "parameters": [ + { + "type": "string", + "description": "Business reference type, e.g. helicopter", + "name": "ref_type", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Business reference ID", + "name": "ref_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Optional attachment category", + "name": "category", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort values: sort_order,-sort_order,created_at,-created_at,updated_at,-updated_at", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerAttachmentListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/attachments/get/{uuid}": { + "get": { + "description": "Get one generic attachment relation by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Get attachment detail", + "operationId": "FileManagerGetAttachment", + "parameters": [ + { + "type": "string", + "description": "Attachment UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerAttachmentResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/delete": { + "delete": { + "description": "Soft-delete multiple nodes by UUID. UUID can be folder or file.\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Delete nodes (file/folder) in bulk", + "operationId": "FileManagerDeleteNodesBulk", + "parameters": [ + { + "description": "Bulk delete request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerNodePurgeBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerMoveNodeBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/cancel-upload": { + "delete": { + "description": "Cancel pending upload intents before file entity creation.\nThis endpoint is intended for pre-create cleanup when FE uploaded to object storage but user cancels flow.\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Cancel upload intent (bulk)", + "operationId": "FileManagerCancelUploadIntentBulk", + "parameters": [ + { + "description": "Bulk cancel upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileCancelUploadBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create": { + "post": { + "description": "Final public control-plane step after direct PUT upload to S3 (bulk contract).\nVerifies uploaded object per item from upload intent, then persists metadata+outbox durably and emits lifecycle status.\nThis endpoint does not upload binary data.\nPublic flow: POST /files/upload (intent+presign) -\u003e PUT to S3 -\u003e POST /files/create.\nfolder_id is optional; null/omitted means create in root folder.\nCanonical JSON:API data.type is \"file_manager_file_create\" (legacy \"file_manager_file\" remains accepted for backward compatibility).\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create file record from uploaded object", + "operationId": "FileManagerCreateFileFromUploadedObject", + "parameters": [ + { + "description": "Bulk create payload for user profile image", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUserBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-aircraft-image": { + "post": { + "description": "Same as bulk create, but folder is resolved using hierarchy \"__system_root_files__\" -\u003e \"aircraft\" -\u003e \"\u003caircraft designation\u003e\".\naircraft_uuid is required per item to resolve designation.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create aircraft image file record from uploaded object", + "operationId": "FileManagerCreateAircraftImageFromUploadedObject", + "parameters": [ + { + "description": "Bulk create payload for aircraft image", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileAircraftBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-base-image": { + "post": { + "description": "Same as bulk create, but folder is resolved using hierarchy \"__system_root_files__\" -\u003e \"bases\" -\u003e \"\u003cbase-type\u003e\" -\u003e \"\u003cbase-name\u003e\".\nbase_uuid is required per item.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create base image file record from uploaded object", + "operationId": "FileManagerCreateBaseImageFromUploadedObject", + "parameters": [ + { + "description": "Bulk create payload for base image", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileBaseBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-dul-image": { + "post": { + "description": "Finalize uploaded DUL image file(s) from upload intent.\nIf dul_uuid is provided, file is created under DUL folder hierarchy.\nIf dul_uuid is omitted, file is still created (unassigned), then can be attached during /api/v1/dul/create via file_uuid.\nPossible status: 201 (all success), 207 (partial success), 400 (invalid json/create failed), 401 (unauthorized), 403 (forbidden), 404 (dul or file resources not found), 409 (upload-intent conflict), 422 (validation), 503 (DUL or file-manager service unavailable).\nPossible error code: INVALID_REQUEST, USER_UNAUTHORIZED, FORBIDDEN_ACCESS, FILE_MANAGER_VALIDATION_FAILED, FILE_MANAGER_FILE_NOT_FOUND, FILE_MANAGER_UPLOAD_INTENT_NOT_FOUND, FILE_MANAGER_UPLOAD_INTENT_CONFLICT, DUL_NOT_FOUND, FILE_MANAGER_STORAGE_UNAVAILABLE, FILE_MANAGER_QUEUE_UNAVAILABLE, FILE_MANAGER_ATTACHMENT_SERVICE_UNAVAILABLE, FILE_MANAGER_ACTION_FAILED.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create DUL image file record from uploaded object", + "parameters": [ + { + "description": "Bulk create payload for DUL image", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileDULBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-from-template": { + "post": { + "description": "Copies a helicopter template file into a new draft file and returns a Collabora/WOPI write session for the newly created file.\nThis endpoint always marks the created file as ` + "`" + `is_template=false` + "`" + ` because the result is a normal working document, not a reusable template.\nThe source template must be a helicopter file that belongs to the same ` + "`" + `helicopter_id` + "`" + ` provided in the request.\n` + "`" + `template_uuid` + "`" + ` is the helicopter-file UUID, not the file UUID.\nRequest fields: ` + "`" + `template_uuid` + "`" + `, ` + "`" + `helicopter_id` + "`" + `, optional ` + "`" + `takeover_id` + "`" + `, and ` + "`" + `name` + "`" + `.\nSuccess response includes the new ` + "`" + `file_id` + "`" + `, Collabora editor action URL, an access token, and the token TTL in milliseconds.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create file from template and start a WOPI edit session", + "parameters": [ + { + "description": "Create-from-template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileTemplateCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileWOPISessionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-mission-file": { + "post": { + "description": "Materializes uploaded objects into folder \"missions/\u003cmission-uuid\u003e\", creates attachments (ref_type=mission), and links them to the mission so they appear in GET mission.\nmission_uuid is required per item.\nPossible error code: MISSION_FILE_INVALID_JSON, MISSION_FILE_VALIDATION, MISSION_FILE_INVALID_MISSION_UUID, MISSION_FILE_MISSION_NOT_FOUND, MISSION_FILE_SERVICE_UNAVAILABLE, MISSION_FILE_ACTION_FAILED, FILE_MANAGER_UPLOAD_INTENT_NOT_FOUND, FILE_MANAGER_STORAGE_UNAVAILABLE, FILE_MANAGER_ACTION_FAILED.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create mission file record from uploaded object", + "operationId": "FileManagerCreateMissionFileFromUploadedObject", + "parameters": [ + { + "description": "Bulk create payload for mission file", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileMissionBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-takeover-file": { + "post": { + "description": "Finalize uploaded takeover file(s) from upload intent. The takeover relation is attached later from file_id during takeover create/update.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create takeover file node from uploaded object", + "parameters": [ + { + "description": "Bulk create payload for takeover file", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileTakeoverBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/create-user-image": { + "post": { + "description": "Same as bulk create, but folder is resolved using hierarchy \"__system_root_files__\" -\u003e \"user\" -\u003e \"\u003cusername\u003e-\u003cuser-uuid\u003e\".\nuser_uuid is required per item to resolve target user folder.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create user profile image file record from uploaded object", + "operationId": "FileManagerCreateUserImageFromUploadedObject", + "parameters": [ + { + "description": "Bulk create payload for user profile image", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUserBulkCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/get/{uuid}": { + "get": { + "description": "Return file metadata by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Get file detail", + "operationId": "FileManagerGetFile", + "parameters": [ + { + "type": "string", + "description": "File UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileDetailResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/update/{uuid}": { + "patch": { + "description": "Update file metadata by UUID (rename and/or move folder).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Update file entity (rename and/or move)", + "operationId": "FileManagerUpdateFileEntity", + "parameters": [ + { + "type": "string", + "description": "File UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API file manager file update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload": { + "post": { + "description": "Control-plane endpoint to create upload intents and presigned PUT URLs for direct upload to S3 (bulk contract).\nBinary does not flow through backend upload endpoint.\nAfter successful PUT to S3, call POST /api/v1/file-manager/files/create with upload_intent_uuid.\nPresigned URL TTL is controlled by backend storage config.\nBulk payload accepts one or many items. Response is item-based and supports partial success.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents + presigned PUT URLs", + "operationId": "FileManagerRequestUploadIntent", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload-aircraft-image": { + "post": { + "description": "Same flow as file upload intent, but dedicated for aircraft image drag-drop flow.\nUse together with /api/v1/file-manager/files/create-aircraft-image to force target folder \"aircraft\".", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents for aircraft drag-drop images", + "operationId": "FileManagerRequestAircraftImageUploadIntent", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload-base-image": { + "post": { + "description": "Same flow as file upload intent, but dedicated for base image flow.\nUse together with /api/v1/file-manager/files/create-base-image to force target folder under bases hierarchy.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents for base images", + "operationId": "FileManagerRequestBaseImageUploadIntent", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload-dul-image": { + "post": { + "description": "Dedicated upload-intent endpoint for DUL image flow.\nPossible status: 201 (all success), 207 (partial success), 400 (invalid json), 401 (unauthorized), 403 (forbidden), 422 (validation), 503 (storage/queue/upload-intent service unavailable).\nPossible error code: INVALID_REQUEST, USER_UNAUTHORIZED, FORBIDDEN_ACCESS, FILE_MANAGER_VALIDATION_FAILED, FILE_MANAGER_STORAGE_UNAVAILABLE, FILE_MANAGER_QUEUE_UNAVAILABLE, FILE_MANAGER_UPLOAD_INTENT_UNAVAILABLE, FILE_MANAGER_ACTION_FAILED.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents for DUL images", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload-mission-file": { + "post": { + "description": "Dedicated upload-intent endpoint for mission file flow.\nUse together with /api/v1/file-manager/files/create-mission-file to attach the file to a mission.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents for mission files", + "operationId": "FileManagerRequestMissionFileUploadIntent", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload-takeover-file": { + "post": { + "description": "Dedicated upload-intent endpoint for takeover file flow.\nUse together with /api/v1/file-manager/files/create-takeover-file to persist the takeover file relation.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents for takeover files", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/upload-user-image": { + "post": { + "description": "Same flow as file upload intent, but dedicated for user profile image flow.\nUse together with /api/v1/file-manager/files/create-user-image to force target folder under user hierarchy.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Request upload intents for user profile images", + "operationId": "FileManagerRequestUserImageUploadIntent", + "parameters": [ + { + "description": "Bulk upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/files/{fileId}/edit-session": { + "get": { + "description": "Returns a Collabora/WOPI write session for an already existing file that belongs to the authenticated user's takeover scope or to the authenticated user as an unattached draft.\nThis endpoint does not create or copy files. It only resolves the file, checks access, and mints a new access token.\nUse this when reopening an existing file for editing.\nPath param ` + "`" + `fileId` + "`" + ` must be a valid UUIDv7.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Start a WOPI edit session for an existing file", + "parameters": [ + { + "type": "string", + "description": "File UUID (UUIDv7)", + "name": "fileId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileWOPISessionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/folders/create": { + "post": { + "description": "Create one folder metadata node under root or under provided parent_id.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Create folder node", + "operationId": "FileManagerCreateFolder", + "parameters": [ + { + "description": "JSON:API file manager folder create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFolderCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFolderResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/folders/get/{uuid}": { + "get": { + "description": "Return folder metadata by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Get folder detail", + "operationId": "FileManagerGetFolder", + "parameters": [ + { + "type": "string", + "description": "Folder UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFolderDetailResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/folders/update/{uuid}": { + "patch": { + "description": "Update folder metadata by UUID (rename and/or move parent).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Update folder node", + "operationId": "FileManagerUpdateFolder", + "parameters": [ + { + "type": "string", + "description": "Folder UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API file manager folder update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFolderUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFolderResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/get-all": { + "get": { + "description": "Return merged direct children (folders + files) for optional parent_uuid (non-recursive).\nIf parent_uuid is omitted, response returns root level nodes.\nIf search is provided, performs a global name search across all folders and files (parent_uuid is ignored).", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "List merged explorer nodes", + "operationId": "FileManagerListMergedExplorerNodes", + "parameters": [ + { + "type": "string", + "description": "Parent folder UUID", + "name": "parent_uuid", + "in": "query" + }, + { + "type": "string", + "description": "Search keyword (global name search across all folders and files)", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerNodeListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/move": { + "patch": { + "description": "Unified move endpoint. Each item UUID can be either folder UUID or file UUID.\ndestination_folder_uuid null/empty means move to root.\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Move nodes (file/folder) in bulk", + "operationId": "FileManagerMoveNodesBulk", + "parameters": [ + { + "description": "Bulk move request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerNodeMoveBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerMoveNodeBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/purge-delete": { + "delete": { + "description": "Permanently delete multiple trashed nodes by UUID. UUID can be folder or file.\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Purge nodes (file/folder) from trash in bulk", + "operationId": "FileManagerPurgeNodesBulkFromTrash", + "parameters": [ + { + "description": "Bulk purge request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerNodePurgeBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerPurgeNodeBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/restore": { + "patch": { + "description": "Restore multiple trashed nodes by UUID to original location.\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "Restore nodes (file/folder) in bulk", + "operationId": "FileManagerRestoreNodesBulk", + "parameters": [ + { + "description": "Bulk restore request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerNodePurgeBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerMoveNodeBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/file-manager/trash/get-all": { + "get": { + "description": "Return merged trashed nodes (folders + files) for trash explorer.", + "produces": [ + "application/json" + ], + "tags": [ + "File Manager" + ], + "summary": "List merged trash items", + "operationId": "FileManagerListMergedTrashItems", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort values: deleted_at,-deleted_at,name,-name,type,-type,size_bytes,-size_bytes", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerTrashListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/fleet-status/create": { + "post": { + "description": "Create fleet status with maintenance schedules and file attachments.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "Create fleet status", + "parameters": [ + { + "description": "Fleet status create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/delete/{id}": { + "delete": { + "description": "Soft delete fleet status by ID.", + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "Delete fleet status", + "parameters": [ + { + "type": "string", + "description": "Fleet status ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/get-all": { + "get": { + "description": "Lists every helicopter from the master, each with its latest fleet status (or empty data when it has none). Paginated by helicopter.", + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "List fleet status", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Page size", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "Search helicopters by designation/identifier/type", + "name": "filter", + "in": "query" + }, + { + "type": "string", + "description": "Sort helicopters, example: designation", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter by operational status; comma-separated for multiple (e.g. available,booked). Values: available, booked, aog, mcf", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "Filter by helicopter ID (UUIDv7); combined with status uses AND logic", + "name": "helicopter_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/get-all/dt": { + "get": { + "description": "Lists every helicopter from the master, each with its latest fleet status (or empty data when it has none). Paginated by helicopter.", + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "List fleet status (datatable)", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Datatable draw counter", + "name": "draw", + "in": "query" + }, + { + "type": "integer", + "default": 0, + "description": "Datatable start offset", + "name": "start", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Datatable page length", + "name": "length", + "in": "query" + }, + { + "type": "string", + "description": "Search helicopters by designation/identifier/type", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Sort helicopters, example: designation", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Filter by operational status; comma-separated for multiple (e.g. available,booked). Values: available, booked, aog, mcf", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "Filter by helicopter ID (UUIDv7); combined with status uses AND logic", + "name": "helicopter_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/get-by-helicopter/{helicopter_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "Get latest fleet status by helicopter ID", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "Get fleet status by ID", + "parameters": [ + { + "type": "string", + "description": "Fleet status ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/history/{helicopter_id}": { + "get": { + "description": "Unified timeline: maintenance servicing events merged with complaint/MEL/NSR/action/MCF/EASA/AOG action events, newest first.", + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "List fleet history (timeline) by helicopter", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Page size", + "name": "page_size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetHistoryListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/mark-serviced/{id}": { + "post": { + "description": "Move current maintenance schedules into service logs, then clear active schedules.", + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "Mark fleet status serviced", + "parameters": [ + { + "type": "string", + "description": "Fleet status ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusMarkServicedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fleet-status/update/{id}": { + "patch": { + "description": "Update fleet status by ID with full replacement for schedules and files.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Fleet Status" + ], + "summary": "Update fleet status", + "parameters": [ + { + "type": "string", + "description": "Fleet status ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Fleet status update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flight-data/create": { + "post": { + "description": "Create a flight data row.\nRequired field: ` + "`" + `mission_id` + "`" + `. ` + "`" + `co_pilot_id` + "`" + ` is optional.\nSwagger request example shows the complete common payload for an ` + "`" + `SPO` + "`" + ` mission. ` + "`" + `SPO` + "`" + ` is the default example shape, and for other mission types (` + "`" + `CAT` + "`" + ` and ` + "`" + `NCO` + "`" + `) you do not need to send the ` + "`" + `hec` + "`" + `, ` + "`" + `heslo` + "`" + `, or ` + "`" + `logging` + "`" + ` blocks. ` + "`" + `rotor_brake_cycle` + "`" + ` is optional for ` + "`" + `SPO` + "`" + `, ` + "`" + `CAT` + "`" + `, and ` + "`" + `NCO` + "`" + `.\nSPO facility references must point to facilities with categories: ` + "`" + `heslo_rope_label_id -\u003e heslo-rope-label` + "`" + `, ` + "`" + `heslo.slings -\u003e heslo-rope-length` + "`" + `, ` + "`" + `logging.slings -\u003e heslo-hook` + "`" + `, ` + "`" + `hec.flights -\u003e hec-rope-label` + "`" + `, and ` + "`" + `hec.slings -\u003e hec-rope-length` + "`" + `.\nSPO-specific response fields (` + "`" + `heslo` + "`" + `, ` + "`" + `logging` + "`" + `, and ` + "`" + `hec` + "`" + `) are returned only when the related mission type is ` + "`" + `SPO` + "`" + `.\nExample request: {\"data\":{\"type\":\"flight_data_create\",\"attributes\":{\"mission_id\":\"019d6774-98e8-7fba-9ef4-3795c0da8a13\"}}}", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Mission - Flight Data" + ], + "summary": "Create flight data", + "parameters": [ + { + "description": "JSON:API flight data create request. Example is for SPO; CAT/NCO can omit SPO-only blocks and other optional fields.", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flight-data/delete/{id}": { + "delete": { + "description": "Soft delete flight data by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "Mission - Flight Data" + ], + "summary": "Delete flight data by ID", + "parameters": [ + { + "type": "string", + "description": "Flight Data UUID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flight-data/get-all": { + "get": { + "description": "List flight data rows. Supports ` + "`" + `date` + "`" + ` for exact flight take-off date filtering (` + "`" + `YYYY-MM-DD` + "`" + `), plus ` + "`" + `mission_id` + "`" + ` and ` + "`" + `flight_data_id` + "`" + ` filters.\nSPO-specific response fields (` + "`" + `heslo` + "`" + `, ` + "`" + `logging` + "`" + `, and ` + "`" + `hec` + "`" + `) are returned only when the related mission type is ` + "`" + `SPO` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Mission - Flight Data" + ], + "summary": "List flight data", + "parameters": [ + { + "type": "string", + "description": "Filter by flight take-off date (YYYY-MM-DD)", + "name": "date", + "in": "query" + }, + { + "type": "string", + "description": "Filter by mission UUID", + "name": "mission_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight-data UUID", + "name": "flight_data_id", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flight-data/get-all/dt": { + "get": { + "description": "Datatable-friendly list of flight data rows. Supports ` + "`" + `date` + "`" + ` for exact flight take-off date filtering (` + "`" + `YYYY-MM-DD` + "`" + `), plus ` + "`" + `mission_id` + "`" + ` and ` + "`" + `flight_data_id` + "`" + ` filters.\nSame payload shape as ` + "`" + `get-all` + "`" + `, but with DataTables paging params (` + "`" + `start` + "`" + `, ` + "`" + `length` + "`" + `, ` + "`" + `draw` + "`" + `).\nSPO-specific response fields (` + "`" + `heslo` + "`" + `, ` + "`" + `logging` + "`" + `, and ` + "`" + `hec` + "`" + `) are returned only when the related mission type is ` + "`" + `SPO` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Mission - Flight Data" + ], + "summary": "List flight data (datatable)", + "parameters": [ + { + "type": "integer", + "description": "DataTables draw counter", + "name": "draw", + "in": "query" + }, + { + "type": "integer", + "description": "Paging start offset", + "name": "start", + "in": "query" + }, + { + "type": "integer", + "description": "Paging length", + "name": "length", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight take-off date (YYYY-MM-DD)", + "name": "date", + "in": "query" + }, + { + "type": "string", + "description": "Filter by mission UUID", + "name": "mission_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight-data UUID", + "name": "flight_data_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flight-data/get/{id}": { + "get": { + "description": "Get flight data detail by ID. Path ` + "`" + `id` + "`" + ` can be mission UUID, flight-data UUID, or flight UUID.\nSPO-specific response fields (` + "`" + `heslo` + "`" + `, ` + "`" + `logging` + "`" + `, and ` + "`" + `hec` + "`" + `) are returned only when the related mission type is ` + "`" + `SPO` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Mission - Flight Data" + ], + "summary": "Get flight data by ID", + "parameters": [ + { + "type": "string", + "description": "Mission UUID / Flight Data UUID / Flight UUID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flight-data/update/{id}": { + "patch": { + "description": "Update a flight data row by UUID.\nRequired field: ` + "`" + `mission_id` + "`" + `. ` + "`" + `co_pilot_id` + "`" + ` is optional.\nSwagger request example uses the same SPO-shaped payload as create, but this endpoint updates an existing flight-data row.\nSPO facility references must point to facilities with categories: ` + "`" + `heslo_rope_label_id -\u003e heslo-rope-label` + "`" + `, ` + "`" + `heslo.slings -\u003e heslo-rope-length` + "`" + `, ` + "`" + `logging.slings -\u003e heslo-hook` + "`" + `, ` + "`" + `hec.flights -\u003e hec-rope-label` + "`" + `, and ` + "`" + `hec.slings -\u003e hec-rope-length` + "`" + `.\nFor frontend usage, send only the fields that need to change; omitted fields stay unchanged. ` + "`" + `SPO` + "`" + ` is the default example shape, and for ` + "`" + `CAT` + "`" + `/` + "`" + `NCO` + "`" + ` you do not need to send the ` + "`" + `hec` + "`" + `, ` + "`" + `heslo` + "`" + `, or ` + "`" + `logging` + "`" + ` blocks. ` + "`" + `rotor_brake_cycle` + "`" + ` is optional for ` + "`" + `SPO` + "`" + `, ` + "`" + `CAT` + "`" + `, and ` + "`" + `NCO` + "`" + `.\nSPO-specific response fields (` + "`" + `heslo` + "`" + `, ` + "`" + `logging` + "`" + `, and ` + "`" + `hec` + "`" + `) are returned only when the related mission type is ` + "`" + `SPO` + "`" + `.\nExample request: {\"data\":{\"type\":\"flight_data_create\",\"attributes\":{\"mission_id\":\"019d6774-98e8-7fba-9ef4-3795c0da8a13\"}}}", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Mission - Flight Data" + ], + "summary": "Update flight data by ID", + "parameters": [ + { + "type": "string", + "description": "Flight Data UUID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API flight data update request. Send only fields you want to change; omit the rest.", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flights/get": { + "get": { + "description": "Returns flights assigned to current auth user, including workflow steps (` + "`" + `duty_roster` + "`" + `, ` + "`" + `reserve_ac` + "`" + `, ` + "`" + `mission` + "`" + `, ` + "`" + `after_flight_inspection` + "`" + `, ` + "`" + `fm_report` + "`" + `).\nEach step includes ` + "`" + `status` + "`" + `, ` + "`" + `completed_at` + "`" + `, and ` + "`" + `completed_by` + "`" + ` for consistent FE rendering.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight" + ], + "summary": "Get flights for current auth user (from duty roster assignment)", + "parameters": [ + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightListResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/flights/get-all": { + "get": { + "description": "Supports ` + "`" + `search` + "`" + `, ` + "`" + `filter[search]` + "`" + `, ` + "`" + `sort` + "`" + `, ` + "`" + `page[number]` + "`" + `, ` + "`" + `page[size]` + "`" + `, ` + "`" + `page` + "`" + `, ` + "`" + `size` + "`" + `, and ` + "`" + `limit` + "`" + `.\nSupports ` + "`" + `date` + "`" + ` and ` + "`" + `filter[date]` + "`" + ` for exact flight date filtering (` + "`" + `YYYY-MM-DD` + "`" + `).\nReturns paginated flights with workflow steps (` + "`" + `takeover` + "`" + `, ` + "`" + `mission` + "`" + `, ` + "`" + `after_flight_inspection` + "`" + `, ` + "`" + `fm_report` + "`" + `).\nEach step includes ` + "`" + `status` + "`" + `, ` + "`" + `completed_at` + "`" + `, and ` + "`" + `completed_by` + "`" + ` for consistent FE rendering.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight" + ], + "summary": "List flights", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search text (JSON:API style)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight date (YYYY-MM-DD)", + "name": "date", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight date (YYYY-MM-DD) (JSON:API style)", + "name": "filter[date]", + "in": "query" + }, + { + "type": "string", + "description": "Sort field", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/complete/{flight_id}": { + "post": { + "description": "Mark an FM report as completed for a flight. Once completed, the report becomes read-only and flight data edits are locked. Requires a verified security PIN: send the one-time token from POST /auth/pin/verify (action 1a75135b-430a-4592-83e1-6d27814185af) in the X-PIN-Verification-Token header.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Complete FM report", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "One-time security-PIN action token", + "name": "X-PIN-Verification-Token", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportResponse" + } + }, + "202": { + "description": "PIN verification required", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/get-all": { + "get": { + "description": "Returns a paginated list of FM reports. Supports ` + "`" + `search` + "`" + `, ` + "`" + `filter[search]` + "`" + `, ` + "`" + `filter[flight_id]` + "`" + `, ` + "`" + `filter[takeover_id]` + "`" + `, ` + "`" + `filter[flight_inspection_id]` + "`" + `, ` + "`" + `filter[flight_date]` + "`" + `, ` + "`" + `filter[has_engine1_gpc_n1]` + "`" + `, ` + "`" + `filter[has_engine1_ptc_n2]` + "`" + `, ` + "`" + `filter[has_engine2_gpc_n1]` + "`" + `, ` + "`" + `filter[has_engine2_ptc_n2]` + "`" + `, ` + "`" + `sort` + "`" + `, ` + "`" + `page[number]` + "`" + `, ` + "`" + `page[size]` + "`" + `, ` + "`" + `page` + "`" + `, ` + "`" + `size` + "`" + `, and ` + "`" + `limit` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "List FM reports", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search text (JSON:API style)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight ID", + "name": "filter[flight_id]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by takeover ID", + "name": "filter[takeover_id]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight inspection ID", + "name": "filter[flight_inspection_id]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight date (YYYY-MM-DD)", + "name": "filter[flight_date]", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter reports with or without engine1_gpc_n1", + "name": "filter[has_engine1_gpc_n1]", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter reports with or without engine1_ptc_n2", + "name": "filter[has_engine1_ptc_n2]", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter reports with or without engine2_gpc_n1", + "name": "filter[has_engine2_gpc_n1]", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter reports with or without engine2_ptc_n2", + "name": "filter[has_engine2_ptc_n2]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order: flight_date, -flight_date, created_at, -created_at, updated_at, -updated_at, engine1_gpc_n1, -engine1_gpc_n1, engine1_ptc_n2, -engine1_ptc_n2, engine2_gpc_n1, -engine2_gpc_n1, engine2_ptc_n2, -engine2_ptc_n2", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/get/{flight_id}": { + "get": { + "description": "Returns a complete FM report for a flight. The persisted table keeps only the report keys; the response preloads flight, missions, flight inspection, and after-flight inspection details. It also embeds ` + "`" + `fleet_status` + "`" + ` (next inspection / maintenance schedules + base hours, component summary, EASA maintenance release) — a snapshot frozen when the report was created at after-flight inspection, so later complaints / maintenance changes do not alter a historical report. ` + "`" + `fleet_status` + "`" + ` is omitted when the helicopter has no fleet status.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Get FM report by flight ID", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/update/{flight_id}": { + "patch": { + "description": "Update only the engine metric fields for an existing FM report. All other FM report fields stay read-only on this endpoint. The response mirrors the GET FM report and embeds the frozen ` + "`" + `fleet_status` + "`" + ` snapshot (next inspection / maintenance schedules + base hours, component summary, EASA maintenance release).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Update FM report metrics", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API FM report update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FMReportUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/{id}": { + "delete": { + "description": "Soft-delete an FM report by ID. Requires a verified security PIN.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Delete FM report", + "parameters": [ + { + "type": "string", + "description": "FM Report ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "One-time security-PIN action token", + "name": "X-PIN-Verification-Token", + "in": "header", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/{id}/attachments": { + "post": { + "description": "After uploading the file to the presigned URL (from /attachments/upload-intent), call this endpoint with the upload_intent_uuid, name, and category to create the file node and attach it to the FM report. Valid categories: additional_fm_report, briefing, weight_balance, ofp.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Create FM report attachment from upload intent", + "parameters": [ + { + "type": "string", + "description": "FM Report ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "body with upload_intent_uuid, name, and category", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/{id}/attachments/upload-intent": { + "post": { + "description": "Returns a presigned PUT URL and upload_intent_uuid. After uploading the file to the presigned URL, call POST /fm-reports/{id}/attachments with the upload_intent_uuid to create the file node and attach it to the report.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Request upload intent for FM report attachment", + "parameters": [ + { + "type": "string", + "description": "FM Report ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "body with name, content_type, size_bytes", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/fm-reports/{id}/attachments/{att_id}": { + "delete": { + "description": "Remove a specific attachment from an FM report. Verifies ownership before deletion.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight Report" + ], + "summary": "Delete attachment from FM report", + "parameters": [ + { + "type": "string", + "description": "FM Report ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Attachment ID (UUIDv7)", + "name": "att_id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/forces-present/create": { + "post": { + "description": "Create a forces present row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Forces Present" + ], + "summary": "Create forces present", + "parameters": [ + { + "description": "JSON:API forces present create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/forces-present/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Forces Present" + ], + "summary": "Delete forces present", + "parameters": [ + { + "type": "string", + "description": "Forces Present UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/forces-present/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, sortkey, -sortkey, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Forces Present" + ], + "summary": "List forces present", + "parameters": [ + { + "type": "string", + "description": "Search by name/note", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentListResponse" + } + } + } + } + }, + "/api/v1/forces-present/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Forces Present" + ], + "summary": "List forces present (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentDataTableResponse" + } + } + } + } + }, + "/api/v1/forces-present/update/{uuid}": { + "patch": { + "description": "Patch forces present by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Forces Present" + ], + "summary": "Update forces present (partial)", + "parameters": [ + { + "type": "string", + "description": "Forces Present UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API forces present update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/health-insurance-companies/create": { + "post": { + "description": "Create a health insurance company row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Health Insurance Companies" + ], + "summary": "Create health insurance company", + "parameters": [ + { + "description": "JSON:API health insurance company create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/health-insurance-companies/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Health Insurance Companies" + ], + "summary": "Delete health insurance company", + "parameters": [ + { + "type": "string", + "description": "Health Insurance Company UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/health-insurance-companies/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, sortkey, -sortkey, state, -state, address, -address, mobile_number, -mobile_number, email, -email, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Health Insurance Companies" + ], + "summary": "List health insurance companies", + "parameters": [ + { + "type": "string", + "description": "Search by name/state/address/mobile/email", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyListResponse" + } + } + } + } + }, + "/api/v1/health-insurance-companies/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Health Insurance Companies" + ], + "summary": "List health insurance companies (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyDataTableResponse" + } + } + } + } + }, + "/api/v1/health-insurance-companies/update/{uuid}": { + "patch": { + "description": "Patch health insurance company by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Health Insurance Companies" + ], + "summary": "Update health insurance company (partial)", + "parameters": [ + { + "type": "string", + "description": "Health Insurance Company UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API health insurance company update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/helicopter-files/cancel-upload": { + "post": { + "description": "Cancel pending upload intents from helicopter file upload flow.\nBulk payload accepts one or many items.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "Cancel helicopter file upload intent (bulk)", + "parameters": [ + { + "description": "Bulk helicopter file cancel upload intent request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileCancelUploadBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-files/create": { + "post": { + "description": "Create helicopter file template record from upload intent.\nSection allowed values: ` + "`" + `bfi` + "`" + ` (Before First Flight Inspection), ` + "`" + `fpwb` + "`" + ` (Flight Preparation and WB), ` + "`" + `afi` + "`" + ` (After Last Flight Inspection).\nRequired attributes: ` + "`" + `section` + "`" + `, ` + "`" + `helicopter_id` + "`" + `, ` + "`" + `upload_intent_uuid` + "`" + `, ` + "`" + `file_name` + "`" + `.\nOptional attributes: ` + "`" + `position` + "`" + `, ` + "`" + `is_required` + "`" + `, ` + "`" + `folder_uuid` + "`" + ` (if omitted, file is created in root folder).\nWhen ` + "`" + `position` + "`" + ` is omitted (or \u003c= 0), backend auto-assigns next position per ` + "`" + `helicopter_id + section` + "`" + `.\nIf file name already exists in target folder, existing file is reused and attached.\nSupports single and bulk payload.\nSingle payload example: ` + "`" + `{\"data\":{\"type\":\"helicopter_file_create\",\"attributes\":{\"section\":\"bfi\",\"position\":1,\"is_required\":true,\"helicopter_id\":\"019d6771-5fb5-7337-837f-bc5ed85181a1\",\"upload_intent_uuid\":\"019d6d0d-2de2-7db1-80a2-2862d43af620\",\"file_name\":\"A.pdf\"}}}` + "`" + `\nBulk payload example: ` + "`" + `{\"data\":[{\"type\":\"helicopter_file_create\",\"attributes\":{\"section\":\"bfi\",\"position\":1,\"is_required\":true,\"helicopter_id\":\"019d6771-5fb5-7337-837f-bc5ed85181a1\",\"upload_intent_uuid\":\"019d6d0d-2de2-7db1-80a2-2862d43af620\",\"file_name\":\"A.pdf\"}}]}` + "`" + `\nNote: OpenAPI 2.0 cannot express object-or-array union in one schema; request schema below represents bulk envelope.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "Create helicopter file", + "parameters": [ + { + "description": "JSON:API helicopter file create request (single object or bulk array)", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileCreateBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCreateBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCreateBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-files/delete": { + "delete": { + "description": "Delete helicopter file template record", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "Delete helicopter file", + "parameters": [ + { + "description": "JSON:API helicopter file delete request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileDeleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-files/get-all": { + "get": { + "description": "List helicopter file template records grouped by helicopter.\nPagination is applied per helicopter group (not per file row), so one helicopter's files stay in the same page.", + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "List helicopter files", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Search text (fallback)", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Sort field", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-files/get/{helicopter_id}": { + "get": { + "description": "Return helicopter file template collection for selected helicopter", + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "Get helicopter files by helicopter ID", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID", + "name": "helicopter_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-files/update": { + "patch": { + "description": "Update a helicopter file template record.\nSection allowed values: ` + "`" + `bfi` + "`" + ` (Before First Flight Inspection), ` + "`" + `fpwb` + "`" + ` (Flight Preparation and WB), ` + "`" + `afi` + "`" + ` (After Last Flight Inspection).\nOptional attributes: ` + "`" + `section` + "`" + `, ` + "`" + `position` + "`" + `, ` + "`" + `is_required` + "`" + `, ` + "`" + `helicopter_id` + "`" + `, ` + "`" + `file_name` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "Update helicopter file", + "parameters": [ + { + "description": "JSON:API helicopter file update request (single object or bulk array)", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileUpdateBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUpdateBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-files/upload": { + "post": { + "description": "Control-plane endpoint for helicopter file upload intent + presigned URL flow.\nPublic flow: POST /api/v1/helicopter-files/upload -\u003e PUT to presigned URL -\u003e POST /api/v1/helicopter-files/create.\nCanonical type is ` + "`" + `helicopter_file_upload` + "`" + `.\n` + "`" + `required_headers` + "`" + ` must be sent exactly as returned when doing PUT to ` + "`" + `upload_url` + "`" + `.\nExample ` + "`" + `required_headers` + "`" + `: ` + "`" + `{\"content-type\":\"application/pdf\",\"x-amz-acl\":\"private\"}` + "`" + `\nSupports single and bulk payload.\nSingle payload example: ` + "`" + `{\"data\":{\"type\":\"helicopter_file_upload\",\"attributes\":{\"name\":\"A.pdf\",\"content_type\":\"application/pdf\",\"size_bytes\":12345}}}` + "`" + `\nBulk payload example: ` + "`" + `{\"data\":[{\"type\":\"helicopter_file_upload\",\"attributes\":{\"name\":\"A.pdf\",\"content_type\":\"application/pdf\",\"size_bytes\":12345}}]}` + "`" + `\nNote: OpenAPI 2.0 cannot express object-or-array union in one schema; request schema below represents bulk envelope.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Master File" + ], + "summary": "Request upload intents + presigned PUT URLs (helicopter file)", + "parameters": [ + { + "description": "Helicopter file upload intent request (single object or bulk array)", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileUploadBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentBulkResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentResponse" + } + }, + "207": { + "description": "Multi-Status", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentBulkResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopter-usage/create": { + "post": { + "description": "Create helicopter usage counters for a helicopter.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Helicopter Usage" + ], + "summary": "Create helicopter usage", + "parameters": [ + { + "description": "JSON:API helicopter usage create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/helicopter-usage/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Helicopter Usage" + ], + "summary": "Delete helicopter usage", + "parameters": [ + { + "type": "string", + "description": "Helicopter Usage UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/helicopter-usage/get-all": { + "get": { + "description": "JSON:API list without pagination. Sort values: helicopter_id, -helicopter_id, total_landing, -total_landing, total_airframe_hours, -total_airframe_hours, total_airframe_cycles, -total_airframe_cycles, total_engine_1_hours, -total_engine_1_hours, total_engine_1_gpc_ng_n1, -total_engine_1_gpc_ng_n1, total_engine_1_ptc_nf_n2, -total_engine_1_ptc_nf_n2, total_engine_1_ccc, -total_engine_1_ccc, total_engine_2_hours, -total_engine_2_hours, total_engine_2_gpc_ng_n1, -total_engine_2_gpc_ng_n1, total_engine_2_ptc_nf_n2, -total_engine_2_ptc_nf_n2, total_engine_2_ccc, -total_engine_2_ccc, total_flight_report, -total_flight_report, total_hook_release, -total_hook_release, total_rotor_brake_cycle, -total_rotor_brake_cycle, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Helicopter Usage" + ], + "summary": "List helicopter usage", + "parameters": [ + { + "type": "string", + "description": "Search by helicopter or usage ID", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageListResponse" + } + } + } + } + }, + "/api/v1/helicopter-usage/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Helicopter Usage" + ], + "summary": "List helicopter usage (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageListResponse" + } + } + } + } + }, + "/api/v1/helicopter-usage/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Helicopter Usage" + ], + "summary": "Get helicopter usage", + "parameters": [ + { + "type": "string", + "description": "Helicopter Usage UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/helicopter-usage/update/{uuid}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helicopter - Helicopter Usage" + ], + "summary": "Update helicopter usage (partial)", + "parameters": [ + { + "type": "string", + "description": "Helicopter Usage UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API helicopter usage update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/helicopters/create": { + "post": { + "description": "Create a helicopter configuration row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "Create helicopter", + "parameters": [ + { + "description": "JSON:API Helicopter create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopters/delete/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "Delete helicopter", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopters/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: designation, -designation, sortkey, -sortkey.", + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "List helicopters", + "parameters": [ + { + "type": "string", + "description": "Search by designation/identifier/type", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by operational status; comma-separated for multiple (e.g. available,booked). Values: available, booked, aog, mcf", + "name": "filter[status]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterListResponse" + } + } + } + } + }, + "/api/v1/helicopters/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "List helicopters (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Filter by operational status; comma-separated for multiple (e.g. available,booked). Values: available, booked, aog, mcf", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterDataTableResponse" + } + } + } + } + }, + "/api/v1/helicopters/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "Get helicopter by ID", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopters/update/{id}": { + "patch": { + "description": "Patch helicopter by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "Update helicopter (partial)", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API Helicopter update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/helicopters/{id}/reports/next-number": { + "post": { + "description": "Returns report number format \u003cidentifier\u003e-NNNN (example: SN0901-0001). The identifier is used as the report-code prefix and the numeric suffix is auto-incremented by backend per helicopter.", + "produces": [ + "application/json" + ], + "tags": [ + "Facility - Helicopters" + ], + "summary": "Generate next helicopter report number", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterNextReportNumberResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/hems-operation-category/create": { + "post": { + "description": "Create a HEMS operation category.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Operation - Category" + ], + "summary": "Create HEMS operation category", + "parameters": [ + { + "description": "JSON:API HEMS operation category create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation-category/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - Category" + ], + "summary": "Delete HEMS operation category", + "parameters": [ + { + "type": "string", + "description": "HEMS operation category UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation-category/get-all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - Category" + ], + "summary": "List HEMS operation categories", + "parameters": [ + { + "type": "string", + "description": "Search by name/type", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryListResponse" + } + } + } + } + }, + "/api/v1/hems-operation-category/get-all/dt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - Category" + ], + "summary": "List HEMS operation categories (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryDataTableResponse" + } + } + } + } + }, + "/api/v1/hems-operation-category/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - Category" + ], + "summary": "Get HEMS operation category by ID", + "parameters": [ + { + "type": "string", + "description": "HEMS operation category UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation-category/update/{uuid}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Operation - Category" + ], + "summary": "Update HEMS operation category (partial)", + "parameters": [ + { + "type": "string", + "description": "HEMS operation category UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API HEMS operation category update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation/create": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operation" + ], + "summary": "Create HEMS operation", + "parameters": [ + { + "description": "JSON:API HEMS operation create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operation" + ], + "summary": "Delete HEMS operation", + "parameters": [ + { + "type": "string", + "description": "HEMS operation UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation/get-all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operation" + ], + "summary": "List HEMS operations", + "parameters": [ + { + "type": "string", + "description": "Search by date", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationListResponse" + } + } + } + } + }, + "/api/v1/hems-operation/get-all/dt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operation" + ], + "summary": "List HEMS operations (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationDataTableResponse" + } + } + } + } + }, + "/api/v1/hems-operation/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operation" + ], + "summary": "Get HEMS operation by ID", + "parameters": [ + { + "type": "string", + "description": "HEMS operation UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operation/update/{uuid}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operation" + ], + "summary": "Update HEMS operation (partial)", + "parameters": [ + { + "type": "string", + "description": "HEMS operation UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API HEMS operation update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operational-data/create": { + "post": { + "description": "Create a HEMS operational data record.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operational Data" + ], + "summary": "Create HEMS operational data", + "parameters": [ + { + "description": "JSON:API HEMS operational data create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operational-data/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operational Data" + ], + "summary": "Delete HEMS operational data", + "parameters": [ + { + "type": "string", + "description": "HEMS operational data UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operational-data/get-all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operational Data" + ], + "summary": "List HEMS operational data", + "parameters": [ + { + "type": "string", + "description": "Search by location/notes/additional_info/terrain_index", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataListResponse" + } + } + } + } + }, + "/api/v1/hems-operational-data/get-all/dt": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operational Data" + ], + "summary": "List HEMS operational data (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataDataTableResponse" + } + } + } + } + }, + "/api/v1/hems-operational-data/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operational Data" + ], + "summary": "Get HEMS operational data by ID", + "parameters": [ + { + "type": "string", + "description": "HEMS operational data UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hems-operational-data/update/{uuid}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Operation - HEMS Operational Data" + ], + "summary": "Update HEMS operational data (partial)", + "parameters": [ + { + "type": "string", + "description": "HEMS operational data UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API HEMS operational data update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hospital/create": { + "post": { + "description": "Create a hospital row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Hospital" + ], + "summary": "Create hospital", + "parameters": [ + { + "description": "JSON:API hospital create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hospital/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Hospital" + ], + "summary": "Delete hospital", + "parameters": [ + { + "type": "string", + "description": "Hospital UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/hospital/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, hospital_name, -hospital_name, sortkey, -sortkey, address, -address, landline_number, -landline_number, mobile_number, -mobile_number, email, -email, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Hospital" + ], + "summary": "List hospital", + "parameters": [ + { + "type": "string", + "description": "Search by hospital name/address/contact", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalListResponse" + } + } + } + } + }, + "/api/v1/hospital/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Hospital" + ], + "summary": "List hospital (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalDataTableResponse" + } + } + } + } + }, + "/api/v1/hospital/update/{uuid}": { + "patch": { + "description": "Patch hospital by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Hospital" + ], + "summary": "Update hospital (partial)", + "parameters": [ + { + "type": "string", + "description": "Hospital UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API hospital update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/icao/create": { + "post": { + "description": "Create an ICAO row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - ICAO" + ], + "summary": "Create ICAO", + "parameters": [ + { + "description": "JSON:API ICAO create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/icao/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - ICAO" + ], + "summary": "Delete ICAO", + "parameters": [ + { + "type": "string", + "description": "ICAO UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/icao/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: icao_code, -icao_code, sortkey, -sortkey, address, -address, landline_number, -landline_number, mobile_number, -mobile_number, email, -email, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - ICAO" + ], + "summary": "List ICAO", + "parameters": [ + { + "type": "string", + "description": "Search by icao_code/address/contact", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOListResponse" + } + } + } + } + }, + "/api/v1/icao/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - ICAO" + ], + "summary": "List ICAO (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAODataTableResponse" + } + } + } + } + }, + "/api/v1/icao/update/{uuid}": { + "patch": { + "description": "Patch ICAO by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - ICAO" + ], + "summary": "Update ICAO (partial)", + "parameters": [ + { + "type": "string", + "description": "ICAO UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API ICAO update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/insurance-patient-data/create": { + "post": { + "description": "Create an InsurancePatientData record.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - InsurancePatientData" + ], + "summary": "Create insurance patient data", + "parameters": [ + { + "description": "JSON:API InsurancePatientData create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/insurance-patient-data/delete/{uuid}": { + "delete": { + "description": "Soft delete InsurancePatientData by ID.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - InsurancePatientData" + ], + "summary": "Delete insurance patient data", + "parameters": [ + { + "type": "string", + "description": "InsurancePatientData UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/insurance-patient-data/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - InsurancePatientData" + ], + "summary": "List insurance patient data", + "parameters": [ + { + "type": "string", + "description": "Search", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataListResponse" + } + } + } + } + }, + "/api/v1/insurance-patient-data/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - InsurancePatientData" + ], + "summary": "List insurance patient data (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataDataTableResponse" + } + } + } + } + }, + "/api/v1/insurance-patient-data/get/{uuid}": { + "get": { + "description": "Get a single InsurancePatientData record by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - InsurancePatientData" + ], + "summary": "Get insurance patient data by ID", + "parameters": [ + { + "type": "string", + "description": "InsurancePatientData UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/insurance-patient-data/update/{uuid}": { + "patch": { + "description": "Patch InsurancePatientData by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - InsurancePatientData" + ], + "summary": "Update insurance patient data (partial)", + "parameters": [ + { + "type": "string", + "description": "InsurancePatientData UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API InsurancePatientData update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/land/create": { + "post": { + "description": "Create a land row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Land" + ], + "summary": "Create land", + "parameters": [ + { + "description": "JSON:API land create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/land/delete/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Land" + ], + "summary": "Delete land", + "parameters": [ + { + "type": "string", + "description": "Land ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/land/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, sortkey, -sortkey, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Land" + ], + "summary": "List land", + "parameters": [ + { + "type": "string", + "description": "Search by name", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandListResponse" + } + } + } + } + }, + "/api/v1/land/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Land" + ], + "summary": "List land (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 1000)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandDataTableResponse" + } + } + } + } + }, + "/api/v1/land/update/{id}": { + "patch": { + "description": "Patch land by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Hospital - Land" + ], + "summary": "Update land (partial)", + "parameters": [ + { + "type": "string", + "description": "Land ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API land update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/master-settings/create": { + "post": { + "description": "Create a master settings row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Master Settings" + ], + "summary": "Create master settings", + "parameters": [ + { + "description": "JSON:API master settings create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/master-settings/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Master Settings" + ], + "summary": "Delete master settings", + "parameters": [ + { + "type": "string", + "description": "Master Settings UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Resource" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/master-settings/get": { + "get": { + "description": "Return singleton master settings object (latest active row).", + "produces": [ + "application/json" + ], + "tags": [ + "Master Settings" + ], + "summary": "Get current master settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/master-settings/sso/microsoft": { + "get": { + "description": "Get Microsoft Entra SSO settings from master settings.", + "produces": [ + "application/json" + ], + "tags": [ + "Master Settings" + ], + "summary": "Get Microsoft Entra SSO settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + }, + "put": { + "description": "Create or update Microsoft Entra SSO settings as master settings.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Master Settings" + ], + "summary": "Upsert Microsoft Entra SSO settings", + "parameters": [ + { + "description": "JSON:API Microsoft Entra SSO upsert request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsMicrosoftEntraUpsertRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/master-settings/update/{uuid}": { + "patch": { + "description": "Patch master settings by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Master Settings" + ], + "summary": "Update master settings (partial)", + "parameters": [ + { + "type": "string", + "description": "Master Settings UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API master settings update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mcf/get-by-helicopter/{helicopter_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCF" + ], + "summary": "List MCFs for a helicopter", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFListResponse" + } + } + } + } + }, + "/api/v1/mcf/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCF" + ], + "summary": "Get MCF by ID", + "parameters": [ + { + "type": "string", + "description": "MCF ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFResponse" + } + } + } + } + }, + "/api/v1/mcf/set/{helicopter_id}": { + "post": { + "description": "Toggles and edits a helicopter's MCF. ` + "`" + `active:true` + "`" + ` (or omitted) ACTIVATES/EDITS it — creates the draft if none is open (201), or UPDATES the open draft with the provided check-flight fields (200; af_hours, landing_count, utc_time, date, result, notes — all optional, partial edits allowed). Finalize it via POST /mcf/sign/{id} (sign:true). ` + "`" + `active:false` + "`" + ` DEACTIVATES it — cancels the open (unsigned) draft (\"tidak jadi\") and releases the MCF status; the cancelled record stays in the list. A signed MCF is a real event and is not cancelled.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MCF" + ], + "summary": "Set a helicopter's MCF Yes/No — draft work endpoint (create/update or cancel)", + "parameters": [ + { + "type": "string", + "description": "Helicopter ID (UUIDv7)", + "name": "helicopter_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API MCF set request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MCFSetRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFResponse" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mcf/sign/{id}": { + "post": { + "description": "Finalizes an MCF's check-flight. ` + "`" + `sign` + "`" + ` (optional, default true): true FINALIZES it — af_hours, landing_count, utc_time, date, result required; \"passed\" clears the aircraft from MCF, \"failed\" keeps it grounded. false UNSIGNS it — reverts a signed MCF back to a draft (keeps the recorded data). Editing draft fields is done via /mcf/set; cancelling via /mcf/set (active:false).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MCF" + ], + "summary": "Sign (finalize) or unsign an MCF — toggle by MCF id", + "parameters": [ + { + "type": "string", + "description": "MCF ID (UUIDv7)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "JSON:API MCF sign request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MCFCompleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/medicine-group/create": { + "post": { + "description": "Create a medicine group row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine Group" + ], + "summary": "Create medicine group", + "parameters": [ + { + "description": "JSON:API medicine group create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine-group/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine Group" + ], + "summary": "Delete medicine group", + "parameters": [ + { + "type": "string", + "description": "Medicine Group UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine-group/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine Group" + ], + "summary": "List medicine groups", + "parameters": [ + { + "type": "string", + "description": "Search by group name", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine-group/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine Group" + ], + "summary": "List medicine groups (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine-group/update/{uuid}": { + "patch": { + "description": "Patch medicine group by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine Group" + ], + "summary": "Update medicine group (partial)", + "parameters": [ + { + "type": "string", + "description": "Medicine Group UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API medicine group update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine/create": { + "post": { + "description": "Create a medicine row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine" + ], + "summary": "Create medicine", + "parameters": [ + { + "description": "JSON:API medicine create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine" + ], + "summary": "Delete medicine", + "parameters": [ + { + "type": "string", + "description": "Medicine UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/medicine/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Search covers name, note, pack, unit, and column. Sort values: name, -name, sortkey, -sortkey, note, -note, pack, -pack, unit, -unit, column, -column, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine" + ], + "summary": "List medicine", + "parameters": [ + { + "type": "string", + "description": "Search by name/note/pack/unit/column", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineListResponse" + } + } + } + } + }, + "/api/v1/medicine/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine" + ], + "summary": "List medicine (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineDataTableResponse" + } + } + } + } + }, + "/api/v1/medicine/update/{uuid}": { + "patch": { + "description": "Patch medicine by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Medicine" + ], + "summary": "Update medicine (partial)", + "parameters": [ + { + "type": "string", + "description": "Medicine UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API medicine update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/mission-type/create": { + "post": { + "description": "Create a mission category master record for use in mission flow.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Create mission type", + "parameters": [ + { + "description": "JSON:API mission type create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionTypeCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionTypeResource" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission-type/get-all": { + "get": { + "description": "Returns all mission categories and their available subtypes.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "List mission types with subtypes", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionTypeListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/create": { + "post": { + "description": "Create a mission and link it to a flight.\n` + "`" + `type` + "`" + ` must be one of: ` + "`" + `HEMS` + "`" + `, ` + "`" + `CAT` + "`" + `, ` + "`" + `SPO` + "`" + `, ` + "`" + `NCO` + "`" + `.\n` + "`" + `subtype_id` + "`" + ` is required when ` + "`" + `type=HEMS` + "`" + ` and must reference mission subcategory under ` + "`" + `HEMS` + "`" + ` category.\n` + "`" + `subtype_id` + "`" + ` must be empty when ` + "`" + `type` + "`" + ` is not ` + "`" + `HEMS` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Create mission", + "parameters": [ + { + "description": "JSON:API mission create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/delete-by-id/{mission_id}": { + "delete": { + "description": "Soft delete mission by its mission UUID. This also soft deletes flight data rows linked to the mission.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Delete mission by mission ID", + "parameters": [ + { + "type": "string", + "description": "Mission ID (UUIDv7)", + "name": "mission_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/delete/{flight_id}": { + "delete": { + "description": "Soft delete mission by linked flight ID.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Delete mission", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/file/{mission_id}/{attachment_id}": { + "delete": { + "description": "Detaches the file (by its attachment id) from the mission and deletes the attachment.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Remove a file from a mission", + "parameters": [ + { + "type": "string", + "description": "Mission UUID", + "name": "mission_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Attachment UUID (the file's attachment_id)", + "name": "attachment_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/get-all": { + "get": { + "description": "Supports ` + "`" + `search` + "`" + `, ` + "`" + `filter[search]` + "`" + `, ` + "`" + `flight_id` + "`" + `, ` + "`" + `sort` + "`" + `, ` + "`" + `page[number]` + "`" + `, ` + "`" + `page[size]` + "`" + `, ` + "`" + `page` + "`" + `, ` + "`" + `size` + "`" + `, and ` + "`" + `limit` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "List missions", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search text (JSON:API style)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by flight UUID", + "name": "flight_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter missions by flight data status: draft, in_progress, or completed", + "name": "flight_data_status", + "in": "query" + }, + { + "type": "string", + "description": "Sort field: type|-type|flight_date|-flight_date|created_at|-created_at|updated_at|-updated_at", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/get-all/dt": { + "get": { + "description": "Supports ` + "`" + `search` + "`" + `, ` + "`" + `filter[search]` + "`" + `, ` + "`" + `date_from` + "`" + `, ` + "`" + `date_to` + "`" + `, ` + "`" + `mission_type` + "`" + `, ` + "`" + `pilot_id` + "`" + `, ` + "`" + `helicopter_id` + "`" + `, ` + "`" + `sort` + "`" + `, ` + "`" + `start` + "`" + `, ` + "`" + `length` + "`" + `, ` + "`" + `draw` + "`" + `, ` + "`" + `page[number]` + "`" + `, ` + "`" + `page[size]` + "`" + `, ` + "`" + `page` + "`" + `, ` + "`" + `size` + "`" + `, and ` + "`" + `limit` + "`" + `.\n` + "`" + `date_from` + "`" + ` and ` + "`" + `date_to` + "`" + ` filter flights by their mission date range (` + "`" + `YYYY-MM-DD` + "`" + `).\n` + "`" + `mission_type` + "`" + ` accepts ` + "`" + `HEMS` + "`" + `, ` + "`" + `CAT` + "`" + `, ` + "`" + `SPO` + "`" + `, or ` + "`" + `NCO` + "`" + `.\n` + "`" + `pilot_id` + "`" + ` filters missions where the pilot is assigned on the linked flight roster.\n` + "`" + `helicopter_id` + "`" + ` filters missions by the helicopter used in the linked flight takeover.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "List missions (datatable)", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search text (JSON:API style)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Filter by linked flight UUID", + "name": "flight_id", + "in": "query" + }, + { + "type": "string", + "description": "Mission date lower bound (YYYY-MM-DD)", + "name": "date_from", + "in": "query" + }, + { + "type": "string", + "description": "Mission date upper bound (YYYY-MM-DD)", + "name": "date_to", + "in": "query" + }, + { + "type": "string", + "description": "Mission type: HEMS|CAT|SPO|NCO", + "name": "mission_type", + "in": "query" + }, + { + "type": "string", + "description": "Pilot user UUID", + "name": "pilot_id", + "in": "query" + }, + { + "type": "string", + "description": "Helicopter UUID", + "name": "helicopter_id", + "in": "query" + }, + { + "type": "string", + "description": "Sort field: type|-type|flight_date|-flight_date|created_at|-created_at|updated_at|-updated_at", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "DataTables start index", + "name": "start", + "in": "query" + }, + { + "type": "integer", + "description": "DataTables page size", + "name": "length", + "in": "query" + }, + { + "type": "integer", + "description": "DataTables draw counter", + "name": "draw", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/get/{flight_id}": { + "get": { + "description": "Get mission detail by linked flight ID.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Get mission by flight ID", + "parameters": [ + { + "type": "string", + "description": "Flight ID (UUIDv7)", + "name": "flight_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/mission/update/{mission_id}": { + "patch": { + "description": "Update mission type/subtype/note by mission ID, and optionally attach files already uploaded via ` + "`" + `upload-mission-file` + "`" + `.\n` + "`" + `type` + "`" + ` must be one of: ` + "`" + `HEMS` + "`" + `, ` + "`" + `CAT` + "`" + `, ` + "`" + `SPO` + "`" + `, ` + "`" + `NCO` + "`" + `.\n` + "`" + `subtype_id` + "`" + ` is required when ` + "`" + `type=HEMS` + "`" + ` and must reference mission subcategory under ` + "`" + `HEMS` + "`" + ` category.\n` + "`" + `subtype_id` + "`" + ` must be empty when ` + "`" + `type` + "`" + ` is not ` + "`" + `HEMS` + "`" + `.\n` + "`" + `files[]` + "`" + ` items reference an ` + "`" + `upload_intent_uuid` + "`" + ` (from ` + "`" + `upload-mission-file` + "`" + `) and an optional display ` + "`" + `name` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Mission" + ], + "summary": "Update mission", + "parameters": [ + { + "type": "string", + "description": "Mission ID (UUIDv7)", + "name": "mission_id", + "in": "path", + "required": true + }, + { + "description": "JSON:API mission update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/motor-reaction/create": { + "post": { + "description": "Create a motor reaction row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Motor Reaction" + ], + "summary": "Create motor reaction", + "parameters": [ + { + "description": "JSON:API motor reaction create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/motor-reaction/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Motor Reaction" + ], + "summary": "Delete motor reaction", + "parameters": [ + { + "type": "string", + "description": "Motor Reaction UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/motor-reaction/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, score, -score, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Motor Reaction" + ], + "summary": "List motor reactions", + "parameters": [ + { + "type": "string", + "description": "Search by reaction name or score", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/motor-reaction/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Motor Reaction" + ], + "summary": "List motor reactions (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionDataTableResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/motor-reaction/update/{uuid}": { + "patch": { + "description": "Patch motor reaction by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Motor Reaction" + ], + "summary": "Update motor reaction (partial)", + "parameters": [ + { + "type": "string", + "description": "Motor Reaction UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API motor reaction update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/opc/create": { + "post": { + "description": "Create an OPC row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - OPC" + ], + "summary": "Create opc", + "parameters": [ + { + "description": "JSON:API OPC create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/opc/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "OPC - OPC" + ], + "summary": "Delete opc", + "parameters": [ + { + "type": "string", + "description": "OPC UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/opc/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: title, -title, sortkey, -sortkey, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - OPC" + ], + "summary": "List opc", + "parameters": [ + { + "type": "string", + "description": "Search by title/note", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcListResponse" + } + } + } + } + }, + "/api/v1/opc/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - OPC" + ], + "summary": "List opc (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcDataTableResponse" + } + } + } + } + }, + "/api/v1/opc/update/{uuid}": { + "patch": { + "description": "Patch opc by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - OPC" + ], + "summary": "Update opc (partial)", + "parameters": [ + { + "type": "string", + "description": "OPC UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API OPC update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/patient-data/create": { + "post": { + "description": "Create a PatientData record.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - PatientData" + ], + "summary": "Create patient data", + "parameters": [ + { + "description": "JSON:API PatientData create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/patient-data/delete/{uuid}": { + "delete": { + "description": "Soft delete PatientData by ID.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - PatientData" + ], + "summary": "Delete patient data", + "parameters": [ + { + "type": "string", + "description": "PatientData UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/patient-data/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: family_name, -family_name, first_name, -first_name, birth_date, -birth_date, gender, -gender, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - PatientData" + ], + "summary": "List patient data", + "parameters": [ + { + "type": "string", + "description": "Search by family_name/first_name/svnr/email/phone/street", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataListResponse" + } + } + } + } + }, + "/api/v1/patient-data/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - PatientData" + ], + "summary": "List patient data (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search by family_name/first_name/svnr/email/phone/street", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataDataTableResponse" + } + } + } + } + }, + "/api/v1/patient-data/get/{uuid}": { + "get": { + "description": "Get a single PatientData record by UUID.", + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - PatientData" + ], + "summary": "Get patient data by ID", + "parameters": [ + { + "type": "string", + "description": "PatientData UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/patient-data/update/{uuid}": { + "patch": { + "description": "Patch PatientData by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PatientData - PatientData" + ], + "summary": "Update patient data (partial)", + "parameters": [ + { + "type": "string", + "description": "PatientData UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API PatientData update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/public/branding/email-logo": { + "get": { + "description": "Return a stable company logo optimized for email clients like Outlook.\nBackend prefers managed raster thumbnail/logo and falls back to backend-generated PNG when necessary.\nSupports conditional requests with ` + "`" + `If-None-Match` + "`" + ` / ` + "`" + `If-Modified-Since` + "`" + `.", + "produces": [ + "image/png", + "image/jpeg", + "image/gif", + "application/octet-stream" + ], + "tags": [ + "Branding" + ], + "summary": "Get email-safe branding logo", + "responses": { + "200": { + "description": "Email-safe branding logo binary", + "schema": { + "type": "file" + } + }, + "304": { + "description": "Not Modified", + "schema": { + "type": "string" + } + } + } + } + }, + "/api/v1/reserve-acs/create": { + "post": { + "description": "Create reserve AC, create draft inspection, fill before + prepare inspection payload, and assign to flight.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Reserve AC" + ], + "summary": "Create reserve aircraft assignment", + "parameters": [ + { + "description": "Create reserve AC request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/reserve-acs/delete": { + "delete": { + "description": "Delete reserve AC by body ` + "`" + `data.id` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Reserve AC" + ], + "summary": "Delete reserve AC", + "parameters": [ + { + "description": "Delete reserve AC request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcDeleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Document" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/reserve-acs/get-all": { + "get": { + "description": "Supports ` + "`" + `search` + "`" + `, ` + "`" + `filter[search]` + "`" + `, ` + "`" + `sort` + "`" + `, ` + "`" + `page[number]` + "`" + `, ` + "`" + `page[size]` + "`" + `, ` + "`" + `page` + "`" + `, ` + "`" + `size` + "`" + `, and ` + "`" + `limit` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Reserve AC" + ], + "summary": "List reserve AC", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search text (JSON:API style)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Reserve AC UUID (exact match)", + "name": "id", + "in": "query" + }, + { + "type": "string", + "description": "Flight UUID (exact match)", + "name": "flight_id", + "in": "query" + }, + { + "type": "string", + "description": "Sort field", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page[size]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (fallback)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (fallback)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/reserve-acs/get-all/dt": { + "get": { + "description": "Datatable list scoped by auth user. Admin can see all rows; non-admin only sees reserve AC linked to own flight assignments.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Reserve AC" + ], + "summary": "List reserve AC (datatable)", + "parameters": [ + { + "type": "string", + "description": "Search text", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Search text (JSON:API style)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "string", + "description": "Reserve AC UUID (exact match)", + "name": "id", + "in": "query" + }, + { + "type": "string", + "description": "Flight UUID (exact match)", + "name": "flight_id", + "in": "query" + }, + { + "type": "string", + "description": "Sort field", + "name": "sort", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/reserve-acs/get/{uuid}": { + "get": { + "description": "Get reserve AC detail. Path ` + "`" + `uuid` + "`" + ` accepts reserve_ac id, and supports fallback to flight id.", + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Reserve AC" + ], + "summary": "Get reserve AC by UUID or flight UUID", + "parameters": [ + { + "type": "string", + "description": "Reserve AC UUID or Flight UUID", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/reserve-acs/update": { + "patch": { + "description": "Update reserve AC using body ` + "`" + `data.id` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flights - Reserve AC" + ], + "summary": "Update reserve AC", + "parameters": [ + { + "description": "Update reserve AC request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/roles/create": { + "post": { + "description": "Create a new role. Name is required and must be unique.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Create role", + "parameters": [ + { + "description": "JSON:API Role create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Delete role", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "List roles", + "parameters": [ + { + "type": "string", + "description": "Filter by name (contains)", + "name": "filter[name]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "description": "Set to 'permissions' to embed each role's assigned permissions", + "name": "include", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleListResponse" + } + } + } + } + }, + "/api/v1/roles/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "List roles (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleDataTableResponse" + } + } + } + } + }, + "/api/v1/roles/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get role by ID", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/permissions/get-all": { + "get": { + "description": "Returns every permission grouped by its module, ordered by the module registry. Optional filter[name] narrows by permission name/key.", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "List permissions grouped by module", + "parameters": [ + { + "type": "string", + "description": "Filter by name/key (contains)", + "name": "filter[name]", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionGroupedResponse" + } + } + } + } + }, + "/api/v1/roles/permissions/{permission_uuid}": { + "patch": { + "description": "Patch permission by ID. Currently supports updating requires_pin.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Update permission", + "parameters": [ + { + "type": "string", + "description": "Permission UUID (UUIDv7)", + "name": "permission_uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API permission update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionResource" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/update/{uuid}": { + "patch": { + "description": "Patch role by ID. Provide at least one of: name, description. Name must be unique if provided.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Update role (partial)", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API Role update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/{uuid}/permissions": { + "get": { + "description": "Returns the permissions currently assigned to the given role. Pair with /roles/permissions/get-all to pre-check a role-permission editor.", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "List a role's assigned permissions", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionListResponse" + } + } + } + }, + "post": { + "description": "Assign one permission to a role. Idempotent if already assigned.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Assign permission to role", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API role permission assign request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionAssignRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/{uuid}/permissions/assign-bulk": { + "post": { + "description": "Assign multiple permissions to role. Idempotent for duplicates/already assigned.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Assign permissions to role (bulk)", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API role permission bulk request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionAssignBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionListResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/{uuid}/permissions/remove-bulk": { + "post": { + "description": "Remove multiple permissions from role.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Remove permissions from role (bulk)", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API role permission bulk request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionRemoveBulkRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionListResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/roles/{uuid}/permissions/{permission_uuid}": { + "delete": { + "description": "Remove one permission from role.", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Remove permission from role", + "parameters": [ + { + "type": "string", + "description": "Role UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Permission UUID (UUIDv7)", + "name": "permission_uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/takeovers/create": { + "post": { + "description": "Atomic create for duty roster + crews + reserve AC + pre-flight inspection + flight. The takeover inspection payload also includes fleet status files under ` + "`" + `inspection.fleet_status_file` + "`" + `.\nOptional takeover file uploads can be included via ` + "`" + `data.attributes.files[*]` + "`" + ` using upload intent UUIDs.\nEdited template draft files created earlier can be linked by passing ` + "`" + `data.attributes.edited_template_file_ids[*]` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flight - Takeovers" + ], + "summary": "Create takeover in one request", + "parameters": [ + { + "description": "Create takeover request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/takeovers/delete/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Flight - Takeovers" + ], + "summary": "Delete takeover by flight UUID", + "parameters": [ + { + "type": "string", + "description": "Flight UUID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/takeovers/get-all": { + "get": { + "description": "Return composite takeover records built from flight, reserve AC, duty roster, and inspection. The inspection payload includes ` + "`" + `before` + "`" + `, ` + "`" + `prepare` + "`" + `, and ` + "`" + `fleet_status_file` + "`" + `.", + "produces": [ + "application/json" + ], + "tags": [ + "Flight - Takeovers" + ], + "summary": "List takeovers", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/takeovers/get/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Flight - Takeovers" + ], + "summary": "Get takeover by flight UUID", + "parameters": [ + { + "type": "string", + "description": "Flight UUID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/takeovers/update/{id}": { + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flight - Takeovers" + ], + "summary": "Replace takeover by flight UUID", + "parameters": [ + { + "type": "string", + "description": "Flight UUID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Update takeover request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorDocument" + } + } + } + } + }, + "/api/v1/users/create": { + "post": { + "description": "Create a new user. Email, username, first name, last name and role_id are required. Use role_ids to assign additional roles.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create user", + "parameters": [ + { + "description": "JSON:API User create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/users/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Delete user", + "parameters": [ + { + "type": "string", + "description": "User UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/users/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: email, -email, first_name, -first_name, last_name, -last_name, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "List users", + "parameters": [ + { + "type": "string", + "description": "Search by username/email/first_name/last_name (contains)", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserListResponse" + } + } + } + } + }, + "/api/v1/users/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "List users (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserDataTableResponse" + } + } + } + } + }, + "/api/v1/users/get/{uuid}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user by ID", + "parameters": [ + { + "type": "string", + "description": "User UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/users/update/{uuid}": { + "patch": { + "description": "Patch user by ID. Provide at least one attribute to update.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update user (partial)", + "parameters": [ + { + "type": "string", + "description": "User UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API User update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/vocation/create": { + "post": { + "description": "Create a vocation row.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Vocation" + ], + "summary": "Create vocation", + "parameters": [ + { + "description": "JSON:API vocation create request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationCreateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/vocation/delete/{uuid}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Vocation" + ], + "summary": "Delete vocation", + "parameters": [ + { + "type": "string", + "description": "Vocation UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + }, + "/api/v1/vocation/get-all": { + "get": { + "description": "JSON:API list with pagination, filtering and sorting. Sort values: name, -name, sortkey, -sortkey, note, -note, is_active, -is_active, created_at, -created_at, updated_at, -updated_at.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Vocation" + ], + "summary": "List vocation", + "parameters": [ + { + "type": "string", + "description": "Search by name/note", + "name": "filter[search]", + "in": "query" + }, + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page[number]", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "page[size]", + "in": "query" + }, + { + "type": "string", + "description": "Sort order", + "name": "sort", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationListResponse" + } + } + } + } + }, + "/api/v1/vocation/get-all/dt": { + "get": { + "description": "Datatable response with simple pagination params.", + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Vocation" + ], + "summary": "List vocation (datatable)", + "parameters": [ + { + "type": "integer", + "description": "Page number (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 20, max 100)", + "name": "size", + "in": "query" + }, + { + "type": "integer", + "description": "Page size override (same as size)", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Draw counter (optional)", + "name": "draw", + "in": "query" + }, + { + "type": "string", + "description": "Search term", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationDataTableResponse" + } + } + } + } + }, + "/api/v1/vocation/update/{uuid}": { + "patch": { + "description": "Patch vocation by ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "OPC - Vocation" + ], + "summary": "Update vocation (partial)", + "parameters": [ + { + "type": "string", + "description": "Vocation UUID (UUIDv7)", + "name": "uuid", + "in": "path", + "required": true + }, + { + "description": "JSON:API vocation update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "internal_transport_http_handlers.loginBrandingContentAttrsDTO": { + "type": "object", + "properties": { + "subtitle": { + "type": "string" + }, + "title": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "internal_transport_http_handlers.loginBrandingContentData": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/internal_transport_http_handlers.loginBrandingContentAttrsDTO" + }, + "type": { + "type": "string" + } + } + }, + "internal_transport_http_handlers.loginBrandingContentDocument": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/internal_transport_http_handlers.loginBrandingContentData" + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionAttributes": { + "type": "object", + "properties": { + "completed": { + "type": "integer", + "example": 1 + }, + "created_at": { + "type": "string", + "example": "2026-04-09T05:00:00Z" + }, + "done_file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionFileChecklistItem" + } + }, + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionFileChecklistItem" + } + }, + "flight_data_count": { + "type": "integer", + "example": 2 + }, + "flight_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionResource" + }, + "flight_inspection_id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "flight_time_seconds": { + "type": "integer", + "example": 9000 + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResource" + }, + "hydraulic_lh_checked": { + "type": "string", + "example": "1" + }, + "hydraulic_rh_checked": { + "type": "string", + "example": "1" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "mission_code": { + "type": "string", + "example": "M-2026-001" + }, + "mission_type": { + "type": "array", + "items": { + "type": "string" + } + }, + "note": { + "type": "string", + "example": "All checks completed" + }, + "oil_engine_nr1_checked": { + "type": "string", + "example": "1" + }, + "oil_engine_nr2_checked": { + "type": "string", + "example": "1" + }, + "oil_transmission_igb_checked": { + "type": "string", + "example": "1" + }, + "oil_transmission_mgb_checked": { + "type": "string", + "example": "1" + }, + "oil_transmission_tgb_checked": { + "type": "string", + "example": "1" + }, + "route": { + "type": "array", + "items": { + "type": "string" + } + }, + "total": { + "type": "integer", + "example": 3 + }, + "total_flight": { + "type": "integer", + "example": 3 + }, + "updated_at": { + "type": "string", + "example": "2026-04-09T05:00:00Z" + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionFileChecklistItem": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "file_name": { + "type": "string", + "example": "After Flight Checklist.pdf" + }, + "helicopter_file_id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-111111111111" + }, + "is_done": { + "type": "boolean", + "example": false + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionAttributes" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "type": { + "type": "string", + "example": "after_flight_inspection" + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionResource" + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionUpsertAttributes": { + "type": "object", + "properties": { + "hydraulic_lh_checked": { + "type": "string" + }, + "hydraulic_rh_checked": { + "type": "string" + }, + "note": { + "type": "string" + }, + "oil_engine_nr1_checked": { + "type": "string" + }, + "oil_engine_nr2_checked": { + "type": "string" + }, + "oil_transmission_igb_checked": { + "type": "string" + }, + "oil_transmission_mgb_checked": { + "type": "string" + }, + "oil_transmission_tgb_checked": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionUpsertData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionUpsertAttributes" + }, + "type": { + "type": "string", + "enum": [ + "after_flight_inspection_upsert", + "after_flight_inspection" + ] + } + } + }, + "wucher_internal_transport_http_dto.AfterFlightInspectionUpsertRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionUpsertData" + } + } + }, + "wucher_internal_transport_http_dto.AirRescuerProfileAttributes": { + "type": "object", + "properties": { + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "responsible_flight_rescuer": { + "type": "boolean" + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.AirRescuerProfileUpdateAttributes": { + "type": "object", + "properties": { + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "responsible_flight_rescuer": { + "type": "boolean" + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.AuthCurrentUserAttributes": { + "type": "object", + "properties": { + "auth_provider": { + "type": "string", + "example": "email" + }, + "dul_bases": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULBase" + } + }, + "email": { + "type": "string", + "example": "user@example.com" + }, + "email_sso": { + "type": "string", + "example": "user.sso@example.com" + }, + "email_verified_at": { + "type": "string", + "example": "2026-02-05T10:00:00Z" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "foto": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserFoto" + }, + "is_previously_configured": { + "type": "boolean", + "example": false + }, + "last_name": { + "type": "string", + "example": "Doe" + }, + "linked_sso": { + "type": "boolean", + "example": false + }, + "login_method": { + "type": "string", + "example": "email" + }, + "microsoft_scope": { + "type": "string", + "example": "openid profile email offline_access User.Read" + }, + "mobile_phone": { + "type": "string", + "example": "+628123456789" + }, + "permission_modules": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionGroupResource" + } + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionMini" + } + }, + "pin_blocked": { + "type": "boolean", + "example": false + }, + "pin_configured": { + "type": "boolean", + "example": false + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleMini" + } + }, + "timezone": { + "type": "string", + "example": "Asia/Jakarta" + }, + "totp_enabled": { + "type": "boolean", + "example": false + }, + "webauthn_configured": { + "type": "boolean", + "example": false + } + } + }, + "wucher_internal_transport_http_dto.AuthCurrentUserResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthCurrentUserAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "user" + } + } + } + } + }, + "wucher_internal_transport_http_dto.AuthSessionDeleteResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_session_delete" + } + } + } + } + }, + "wucher_internal_transport_http_dto.AuthSessionItem": { + "type": "object", + "properties": { + "active_now": { + "type": "boolean", + "example": true + }, + "created_at": { + "type": "string", + "example": "2026-03-13T12:00:00Z" + }, + "device_name": { + "type": "string", + "example": "Chrome on macOS" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "ip_address": { + "type": "string", + "example": "127.0.0.1" + }, + "last_active_at": { + "type": "string", + "example": "2026-03-13T12:10:00Z" + }, + "user_agent": { + "type": "string", + "example": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..." + } + } + }, + "wucher_internal_transport_http_dto.AuthSessionsResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "sessions": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AuthSessionItem" + } + } + } + }, + "type": { + "type": "string", + "example": "auth_sessions" + } + } + } + } + }, + "wucher_internal_transport_http_dto.BaseAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Lude Airfield" + }, + "base": { + "type": "string", + "example": "Lude" + }, + "base_abbreviation": { + "type": "string", + "example": "LOIG" + }, + "base_category": { + "type": "string", + "enum": [ + "regular", + "hems" + ], + "example": "regular" + }, + "checklist": { + "type": "boolean", + "example": false + }, + "control_center": { + "type": "boolean", + "example": false + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "default_end_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "default_start_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "dry": { + "type": "boolean", + "example": false + }, + "dul": { + "type": "boolean", + "example": false + }, + "email": { + "type": "string", + "example": "lude@wucher.local" + }, + "foto": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseFoto" + }, + "hems_edc": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseContact" + } + }, + "hems_edc_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_active": { + "type": "boolean", + "example": true + }, + "landline_number": { + "type": "string", + "example": "+62-21-1234" + }, + "latitude": { + "type": "number", + "example": 47.3769 + }, + "leg_time": { + "type": "string", + "example": "00:20" + }, + "longitude": { + "type": "number", + "example": 8.5417 + }, + "med_pax": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseContact" + } + }, + "med_pax_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "mobile_number": { + "type": "string", + "example": "+62-812-1234" + }, + "notes": { + "type": "string", + "example": "Main base" + }, + "operational_shift_times": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseOperationalShiftTime" + } + }, + "responsible_pilot_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "responsible_pilots": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseContact" + } + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseShiftTime" + }, + "sms_alert": { + "type": "boolean", + "example": false + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "utc": { + "type": "string", + "example": "0" + } + } + }, + "wucher_internal_transport_http_dto.BaseContact": { + "type": "object", + "properties": { + "first_name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "last_name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.BaseCreateAttributes": { + "type": "object", + "required": [ + "base", + "latitude", + "longitude" + ], + "properties": { + "address": { + "type": "string", + "example": "Lude Airfield" + }, + "base": { + "type": "string", + "example": "Lude" + }, + "base_abbreviation": { + "type": "string", + "example": "LOIG" + }, + "base_category": { + "type": "string", + "enum": [ + "regular", + "hems" + ], + "example": "regular" + }, + "checklist": { + "type": "boolean", + "example": false + }, + "control_center": { + "type": "boolean", + "example": false + }, + "default_end_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "default_shift_end": { + "type": "string", + "example": "21:00:00" + }, + "default_shift_start": { + "type": "string", + "example": "06:00:00" + }, + "default_shift_time": { + "type": "string", + "example": "12:00" + }, + "default_start_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "dry": { + "type": "boolean", + "example": false + }, + "dul": { + "type": "boolean", + "example": false + }, + "email": { + "type": "string", + "example": "lude@wucher.local" + }, + "file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90" + }, + "hems_edc_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_active": { + "type": "boolean", + "example": true + }, + "landline_number": { + "type": "string", + "example": "+62-21-1234" + }, + "latitude": { + "type": "number", + "maximum": 90, + "minimum": -90, + "example": 47.3769 + }, + "leg_time": { + "type": "string", + "example": "00:20" + }, + "longitude": { + "type": "number", + "maximum": 180, + "minimum": -180, + "example": 8.5417 + }, + "med_pax_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "mobile_number": { + "type": "string", + "example": "+62-812-1234" + }, + "notes": { + "type": "string", + "example": "Main base" + }, + "operational_shift_times": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseOperationalShiftTime" + } + }, + "responsible_pilot_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseShiftTime" + }, + "sms_alert": { + "type": "boolean", + "example": false + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "utc": { + "type": "string", + "example": "0" + } + } + }, + "wucher_internal_transport_http_dto.BaseCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "base" + ], + "example": "base" + } + } + }, + "wucher_internal_transport_http_dto.BaseCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseCreateData" + } + } + }, + "wucher_internal_transport_http_dto.BaseDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.BaseDeleteAttributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto.BaseDeleteResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseDeleteAttributes" + }, + "id": { + "type": "string", + "example": "019d6b10-a385-7e9e-a553-8e892bcec0ef" + }, + "type": { + "type": "string", + "example": "base_delete" + } + } + }, + "wucher_internal_transport_http_dto.BaseDeleteResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseDeleteResource" + } + } + }, + "wucher_internal_transport_http_dto.BaseFoto": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://storage.example.com/base/lude.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026X-Amz-Expires=900\u0026X-Amz-Signature=abc123" + }, + "original_size_bytes": { + "type": "integer", + "example": 1024000 + }, + "thumbnail_download_url": { + "type": "string", + "example": "https://storage.example.com/base/lude.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026X-Amz-Expires=900\u0026X-Amz-Signature=abc123" + }, + "uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.BaseListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.BaseOperationalShiftTime": { + "type": "object", + "properties": { + "date_end": { + "type": "string", + "example": "2026-04-12" + }, + "date_start": { + "type": "string", + "example": "2026-04-12" + }, + "end_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseShiftTime" + }, + "start_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + } + } + }, + "wucher_internal_transport_http_dto.BaseResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "base" + } + } + }, + "wucher_internal_transport_http_dto.BaseResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseResource" + } + } + }, + "wucher_internal_transport_http_dto.BaseShiftTime": { + "type": "object", + "properties": { + "end": { + "type": "string", + "example": "21:00" + }, + "start": { + "type": "string", + "example": "06:00" + } + } + }, + "wucher_internal_transport_http_dto.BaseUpdateAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Lude Airfield" + }, + "base": { + "type": "string", + "example": "Lude" + }, + "base_abbreviation": { + "type": "string", + "example": "LOIG" + }, + "base_category": { + "type": "string", + "enum": [ + "regular", + "hems" + ], + "example": "regular" + }, + "checklist": { + "type": "boolean", + "example": false + }, + "control_center": { + "type": "boolean", + "example": false + }, + "default_end_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "default_shift_end": { + "type": "string", + "example": "21:00:00" + }, + "default_shift_start": { + "type": "string", + "example": "06:00:00" + }, + "default_shift_time": { + "type": "string", + "example": "12:00" + }, + "default_start_time_type": { + "type": "string", + "enum": [ + "FIXED", + "BMCT", + "ECET" + ], + "example": "FIXED" + }, + "dry": { + "type": "boolean", + "example": false + }, + "dul": { + "type": "boolean", + "example": false + }, + "email": { + "type": "string", + "example": "lude@wucher.local" + }, + "file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90" + }, + "hems_edc_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_active": { + "type": "boolean", + "example": true + }, + "landline_number": { + "type": "string", + "example": "+62-21-1234" + }, + "latitude": { + "type": "number", + "maximum": 90, + "minimum": -90, + "example": 47.3769 + }, + "leg_time": { + "type": "string", + "example": "00:20" + }, + "longitude": { + "type": "number", + "maximum": 180, + "minimum": -180, + "example": 8.5417 + }, + "med_pax_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "mobile_number": { + "type": "string", + "example": "+62-812-1234" + }, + "notes": { + "type": "string", + "example": "Main base" + }, + "operational_shift_times": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseOperationalShiftTime" + } + }, + "responsible_pilot_contact_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseShiftTime" + }, + "sms_alert": { + "type": "boolean", + "example": false + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "utc": { + "type": "string", + "example": "0" + } + } + }, + "wucher_internal_transport_http_dto.BaseUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "base_update" + ], + "example": "base_update" + } + } + }, + "wucher_internal_transport_http_dto.BaseUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.BaseUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactBaseRole": { + "type": "object", + "properties": { + "base_id": { + "type": "string" + }, + "base_name": { + "type": "string" + }, + "role_code": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactBulkUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "user_ids" + ], + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "user_ids": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + }, + "example": [ + "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1" + ] + } + } + }, + "type": { + "type": "string", + "example": "contact_bulk_update" + } + } + }, + "wucher_internal_transport_http_dto.ContactBulkUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactBulkUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactChiefFlagsUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "is_chief_physician": { + "type": "boolean" + }, + "is_chief_pilot": { + "type": "boolean" + }, + "is_chief_technician": { + "type": "boolean" + } + } + }, + "type": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactChiefFlagsUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactChiefFlagsUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactCreateAttributes": { + "type": "object", + "properties": { + "air_rescuer": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AirRescuerProfileAttributes" + }, + "doctor": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DoctorProfileAttributes" + }, + "email": { + "type": "string" + }, + "file_uuid": { + "type": "string", + "maxLength": 64 + }, + "first_name": { + "type": "string", + "maxLength": 100 + }, + "flight_assistant": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightAssistantProfileAttributes" + }, + "is_active": { + "type": "boolean" + }, + "is_admin": { + "type": "boolean" + }, + "last_name": { + "type": "string", + "maxLength": 100 + }, + "mobile_phone": { + "type": "string", + "maxLength": 32 + }, + "pilot": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PilotProfileAttributes" + }, + "role_id": { + "type": "string", + "example": "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1" + }, + "role_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1", + "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d2" + ] + }, + "sortkey": { + "type": "integer" + }, + "sso_email": { + "type": "string" + }, + "staff": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.StaffProfileAttributes" + }, + "technician": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TechnicianProfileAttributes" + }, + "timezone": { + "type": "string", + "maxLength": 64 + }, + "username": { + "type": "string", + "maxLength": 100 + } + } + }, + "wucher_internal_transport_http_dto.ContactCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactCreateAttributes" + }, + "type": { + "type": "string", + "example": "contact_create" + } + } + }, + "wucher_internal_transport_http_dto.ContactCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactCreateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListItem" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.ContactLicenseUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "license_no": { + "type": "string" + } + } + }, + "type": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactLicenseUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactLicenseUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactListAttributes": { + "type": "object", + "properties": { + "air_rescuer": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AirRescuerProfileAttributes" + }, + "base_roles": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactBaseRole" + } + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "doctor": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DoctorProfileAttributes" + }, + "email": { + "type": "string" + }, + "first_name": { + "type": "string" + }, + "flight_assistant": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightAssistantProfileAttributes" + }, + "is_active": { + "type": "boolean" + }, + "is_admin": { + "type": "boolean" + }, + "last_name": { + "type": "string" + }, + "license_no": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mobile_phone": { + "type": "string" + }, + "note": { + "type": "string" + }, + "pilot": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PilotProfileAttributes" + }, + "pilot_category": { + "type": "string" + }, + "role_code": { + "type": "string" + }, + "role_name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactRole" + } + }, + "set_password": { + "type": "boolean" + }, + "short_name": { + "type": "string" + }, + "sortkey": { + "type": "integer" + }, + "sso_email": { + "type": "string" + }, + "staff": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.StaffProfileAttributes" + }, + "technician": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TechnicianProfileAttributes" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactListItem": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "email": { + "type": "string" + }, + "first_name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_admin": { + "type": "boolean" + }, + "last_name": { + "type": "string" + }, + "license_no": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mobile_phone": { + "type": "string" + }, + "note": { + "type": "string" + }, + "pilot_category": { + "type": "string" + }, + "role_code": { + "type": "string" + }, + "role_name": { + "type": "string" + }, + "set_password": { + "type": "boolean" + }, + "short_name": { + "type": "string" + }, + "sortkey": { + "type": "integer" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactListAttributes" + }, + "id": { + "type": "string", + "example": "019dcce4-f15e-7685-8e4c-76f8ad20594b" + }, + "type": { + "type": "string", + "example": "contact" + } + } + } + }, + "meta": { + "type": "object", + "properties": { + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + }, + "wucher_internal_transport_http_dto.ContactPilotCategoryUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "pilot_category" + ], + "properties": { + "pilot_category": { + "type": "string", + "enum": [ + "regular", + "freelance", + "dry_lease" + ] + } + } + }, + "type": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactPilotCategoryUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactPilotCategoryUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactRole": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role_code": { + "type": "string" + }, + "role_name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactRoleUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "role_code" + ], + "properties": { + "role_code": { + "type": "string", + "enum": [ + "pilot", + "doctor", + "air_rescuer", + "technician", + "flight_assistant", + "staff" + ] + } + } + }, + "type": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactRoleUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactRoleUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactSimpleSuccessResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "deleted": { + "type": "boolean" + }, + "updated": { + "type": "boolean" + } + } + }, + "type": { + "type": "string" + } + } + } + } + }, + "wucher_internal_transport_http_dto.ContactStatusUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "is_active": { + "type": "boolean" + } + } + }, + "type": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.ContactStatusUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactStatusUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ContactUpdateAttributes": { + "type": "object", + "properties": { + "air_rescuer": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AirRescuerProfileUpdateAttributes" + }, + "doctor": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DoctorProfileUpdateAttributes" + }, + "email": { + "type": "string" + }, + "file_uuid": { + "type": "string", + "maxLength": 64 + }, + "first_name": { + "type": "string", + "maxLength": 100 + }, + "flight_assistant": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightAssistantProfileUpdateAttributes" + }, + "is_active": { + "type": "boolean" + }, + "is_admin": { + "type": "boolean" + }, + "last_name": { + "type": "string", + "maxLength": 100 + }, + "mobile_phone": { + "type": "string", + "maxLength": 32 + }, + "pilot": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PilotProfileUpdateAttributes" + }, + "role_id": { + "type": "string", + "example": "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1" + }, + "role_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1", + "0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d2" + ] + }, + "sortkey": { + "type": "integer" + }, + "sso_email": { + "type": "string" + }, + "staff": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.StaffProfileUpdateAttributes" + }, + "technician": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TechnicianProfileUpdateAttributes" + }, + "timezone": { + "type": "string", + "maxLength": 64 + }, + "username": { + "type": "string", + "maxLength": 100 + } + } + }, + "wucher_internal_transport_http_dto.ContactUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactUpdateAttributes" + }, + "type": { + "type": "string", + "example": "contact_update" + } + } + }, + "wucher_internal_transport_http_dto.ContactUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ContactUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.DULAttributes": { + "type": "object", + "properties": { + "base": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULBaseRef" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "date": { + "type": "string", + "example": "2026-05-25" + }, + "images": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULImage" + } + }, + "info": { + "type": "string" + }, + "name": { + "type": "string" + }, + "no": { + "type": "integer" + }, + "updated_at": { + "type": "string" + }, + "updated_by": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.DULBase": { + "type": "object", + "properties": { + "category": { + "type": "string", + "example": "hems" + }, + "id": { + "type": "string", + "example": "0197f3b2-c4d8-4b12-a1f9-c1de23456789" + }, + "name": { + "type": "string", + "example": "Halim Base" + } + } + }, + "wucher_internal_transport_http_dto.DULBaseRef": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.DULCreateAttributes": { + "type": "object", + "required": [ + "base_id", + "date", + "name" + ], + "properties": { + "base_id": { + "type": "string", + "example": "019e3ec1-94f6-7b6b-9cc5-1b5d05f46d74" + }, + "date": { + "type": "string", + "example": "2026-05-25" + }, + "file_uuid": { + "type": "array", + "items": { + "type": "string" + } + }, + "info": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 128 + } + } + }, + "wucher_internal_transport_http_dto.DULCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "dul_create", + "dul" + ] + } + } + }, + "wucher_internal_transport_http_dto.DULCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULCreateData" + } + } + }, + "wucher_internal_transport_http_dto.DULDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULGroup" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "records_filtered": { + "type": "integer", + "example": 120 + }, + "records_total": { + "type": "integer", + "example": 120 + }, + "total": { + "type": "integer", + "example": 120 + }, + "total_bases": { + "type": "integer", + "example": 8 + } + } + } + } + }, + "wucher_internal_transport_http_dto.DULDeleteResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "dul_delete" + } + } + } + } + }, + "wucher_internal_transport_http_dto.DULGroup": { + "type": "object", + "properties": { + "base": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULBaseRef" + }, + "duls": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULResource" + } + } + } + }, + "wucher_internal_transport_http_dto.DULImage": { + "type": "object", + "properties": { + "download_url": { + "type": "string" + }, + "original_size_bytes": { + "type": "integer" + }, + "thumbnail_download_url": { + "type": "string" + }, + "uuid": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.DULListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULGroup" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + }, + "total_bases": { + "type": "integer", + "example": 8 + } + } + } + } + }, + "wucher_internal_transport_http_dto.DULResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULAttributes" + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.DULResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULResource" + } + } + }, + "wucher_internal_transport_http_dto.DULUpdateAttributes": { + "type": "object", + "properties": { + "base_id": { + "type": "string" + }, + "date": { + "type": "string", + "example": "2026-05-25" + }, + "file_uuid": { + "type": "array", + "items": { + "type": "string" + } + }, + "info": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.DULUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULUpdateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "dul_update", + "dul" + ] + } + } + }, + "wucher_internal_transport_http_dto.DULUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.DoctorProfileAttributes": { + "type": "object", + "properties": { + "is_chief_physician": { + "type": "boolean" + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "specialization": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + }, + "sub_specialization": { + "type": "string", + "maxLength": 120 + } + } + }, + "wucher_internal_transport_http_dto.DoctorProfileUpdateAttributes": { + "type": "object", + "properties": { + "is_chief_physician": { + "type": "boolean" + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "specialization": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + }, + "sub_specialization": { + "type": "string", + "maxLength": 120 + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPSendAttributes": { + "type": "object", + "required": [ + "challenge_token" + ], + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPSendData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPSendAttributes" + }, + "type": { + "type": "string", + "example": "auth_email_otp_send" + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPSendRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPSendData" + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPSendResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "sent": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_email_otp_send" + } + } + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPVerifyAttributes": { + "type": "object", + "required": [ + "challenge_token", + "code" + ], + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "code": { + "type": "string", + "example": "123456" + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPVerifyData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPVerifyAttributes" + }, + "type": { + "type": "string", + "example": "auth_email_otp_verify" + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPVerifyRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.EmailOTPVerifyData" + } + } + }, + "wucher_internal_transport_http_dto.EmailOTPVerifyResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "requires_totp_setup": { + "type": "boolean", + "example": true + }, + "verified": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_email_otp_verify" + } + } + } + } + }, + "wucher_internal_transport_http_dto.ErrorResponse": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "email is required" + }, + "source": { + "type": "object", + "properties": { + "pointer": { + "type": "string", + "example": "/data/attributes/email" + } + } + }, + "status": { + "type": "string", + "example": "422" + }, + "title": { + "type": "string", + "example": "Validation error" + } + } + } + } + } + }, + "wucher_internal_transport_http_dto.ExchangeAttributes": { + "type": "object", + "required": [ + "access_token", + "id_token" + ], + "properties": { + "access_token": { + "type": "string", + "example": "eyJ0eXAiOiJKV1Qi..." + }, + "id_token": { + "type": "string", + "example": "eyJ0eXAiOiJKV1Qi..." + }, + "id_token_claims": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ExchangeIDTokenClaims" + } + } + }, + "wucher_internal_transport_http_dto.ExchangeData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ExchangeAttributes" + }, + "type": { + "type": "string", + "example": "auth_exchange" + } + } + }, + "wucher_internal_transport_http_dto.ExchangeIDTokenClaims": { + "type": "object", + "properties": { + "aud": { + "type": "string", + "example": "12345678-1234-1234-1234-123456789012" + }, + "email": { + "type": "string", + "example": "andrio@mybit-innovation.com" + }, + "exp": { + "type": "integer", + "example": 1715603600 + }, + "iat": { + "type": "integer", + "example": 1715600000 + }, + "iss": { + "type": "string", + "example": "https://login.microsoftonline.com/abc-tenant-id/v2.0" + }, + "name": { + "type": "string", + "example": "Andrio Effendi" + }, + "nbf": { + "type": "integer", + "example": 1715600000 + }, + "oid": { + "type": "string", + "example": "00000000-0000-0000-1234-567890abcdef" + }, + "preferred_username": { + "type": "string", + "example": "andrio@mybit-innovation.com" + }, + "sub": { + "type": "string", + "example": "AbCdEfGhIjKlMnOpQrStUvWxYz" + }, + "tid": { + "type": "string", + "example": "abc-tenant-id" + }, + "ver": { + "type": "string", + "example": "2.0" + } + } + }, + "wucher_internal_transport_http_dto.ExchangeRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ExchangeData" + } + } + }, + "wucher_internal_transport_http_dto.FMReportAirframeValues": { + "type": "object", + "properties": { + "cycles": { + "type": "number", + "example": 320 + }, + "hours": { + "type": "number", + "example": 1523.5 + } + } + }, + "wucher_internal_transport_http_dto.FMReportAttachmentItem": { + "type": "object", + "properties": { + "download_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "id": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto.FMReportAttachments": { + "type": "object", + "properties": { + "additional_fm_report": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAttachmentItem" + } + }, + "briefing": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAttachmentItem" + } + }, + "ofp": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAttachmentItem" + } + }, + "weight_balance": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAttachmentItem" + } + } + } + }, + "wucher_internal_transport_http_dto.FMReportAttributes": { + "type": "object", + "properties": { + "after_flight_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.AfterFlightInspectionResource" + }, + "completed_at": { + "type": "string", + "example": "2026-04-20T10:15:00Z" + }, + "completed_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "created_at": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "engine1_gpc_n1": { + "type": "string", + "example": "GPC/N1 63.2" + }, + "engine1_ptc_n2": { + "type": "string", + "example": "PTC/N2 61.8" + }, + "engine2_gpc_n1": { + "type": "string", + "example": "GPC/N1 62.9" + }, + "engine2_ptc_n2": { + "type": "string", + "example": "PTC/N2 61.5" + }, + "fleet_status": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResource" + }, + "flight": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightResource" + }, + "flight_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "flight_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionResource" + }, + "flight_inspection_id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "fm_report_attachments": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAttachments" + }, + "helicopter_identifier": { + "type": "string", + "example": "OE-TEST" + }, + "helicopter_usage": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageResource" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "missions": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResource" + } + }, + "pilot_name": { + "type": "string", + "example": "John Doe" + }, + "report_code": { + "type": "string", + "example": "EC-135-1" + }, + "takeover_files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverFileResource" + } + }, + "takeover_id": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + }, + "updated_at": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "usage_breakdown": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportUsageBreakdown" + } + } + }, + "wucher_internal_transport_http_dto.FMReportCountersValues": { + "type": "object", + "properties": { + "flight_reports": { + "type": "number", + "example": 80 + }, + "hook_releases": { + "type": "number", + "example": 10 + }, + "landings": { + "type": "number", + "example": 120 + }, + "rotor_brake_cycles": { + "type": "number", + "example": 8 + } + } + }, + "wucher_internal_transport_http_dto.FMReportEngineValues": { + "type": "object", + "properties": { + "ccc": { + "type": "number", + "example": 44 + }, + "gpc_ng_n1": { + "type": "number", + "example": 63.2 + }, + "hours": { + "type": "number", + "example": 1523.5 + }, + "ptc_nf_n2": { + "type": "number", + "example": 61.8 + } + } + }, + "wucher_internal_transport_http_dto.FMReportListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto.FMReportResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAttributes" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "type": { + "type": "string", + "example": "fm_report" + } + } + }, + "wucher_internal_transport_http_dto.FMReportResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportResource" + } + } + }, + "wucher_internal_transport_http_dto.FMReportUsageBreakdown": { + "type": "object", + "properties": { + "prev": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportUsageSnapshot" + }, + "today": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportUsageSnapshot" + }, + "total": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportUsageSnapshot" + } + } + }, + "wucher_internal_transport_http_dto.FMReportUsageSnapshot": { + "type": "object", + "properties": { + "airframe": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportAirframeValues" + }, + "counters": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportCountersValues" + }, + "engine_1": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportEngineValues" + }, + "engine_2": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FMReportEngineValues" + } + } + }, + "wucher_internal_transport_http_dto.FacilityAttributes": { + "type": "object", + "properties": { + "category": { + "type": "string", + "example": "heslo-rope-label" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "electric": { + "type": "string", + "example": "available" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "length": { + "type": "string", + "example": "10m" + }, + "name": { + "type": "string", + "example": "Fuel Truck Rope Label" + }, + "note": { + "type": "string", + "example": "Dry lease fuel truck" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "type": { + "type": "string", + "example": "Sling" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "weight": { + "type": "string", + "example": "10kg" + } + } + }, + "wucher_internal_transport_http_dto.FacilityCreateAttributes": { + "type": "object", + "required": [ + "category", + "name" + ], + "properties": { + "category": { + "type": "string", + "example": "heslo-rope-label" + }, + "electric": { + "type": "string", + "example": "available" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "length": { + "type": "string", + "example": "10m" + }, + "name": { + "type": "string", + "example": "Fuel Truck Rope Label" + }, + "note": { + "type": "string", + "example": "Dry lease fuel truck" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "type": { + "type": "string", + "example": "Sling" + }, + "weight": { + "type": "string", + "example": "10kg" + } + } + }, + "wucher_internal_transport_http_dto.FacilityCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "facility_create", + "facility" + ], + "example": "facility_create" + } + } + }, + "wucher_internal_transport_http_dto.FacilityCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityCreateData" + } + } + }, + "wucher_internal_transport_http_dto.FacilityDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.FacilityDeleteResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "facility_delete" + } + } + } + } + }, + "wucher_internal_transport_http_dto.FacilityListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.FacilityResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "facility" + } + } + }, + "wucher_internal_transport_http_dto.FacilityResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityResource" + } + } + }, + "wucher_internal_transport_http_dto.FacilityUpdateAttributes": { + "type": "object", + "properties": { + "category": { + "type": "string", + "example": "heslo-rope-label" + }, + "electric": { + "type": "string", + "example": "available" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "length": { + "type": "string", + "example": "10m" + }, + "name": { + "type": "string", + "example": "Fuel Truck Rope Label" + }, + "note": { + "type": "string", + "example": "Dry lease fuel truck" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "type": { + "type": "string", + "example": "Sling" + }, + "weight": { + "type": "string", + "example": "10kg" + } + } + }, + "wucher_internal_transport_http_dto.FacilityUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "facility_update", + "facility" + ], + "example": "facility_update" + } + } + }, + "wucher_internal_transport_http_dto.FacilityUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FacilityUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "land_iso_code": { + "type": "string", + "example": "DE" + }, + "land_name": { + "type": "string", + "example": "Germany" + }, + "name": { + "type": "string", + "example": "Bayern" + }, + "note": { + "type": "string", + "example": "Central region" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateCreateAttributes": { + "type": "object", + "required": [ + "land_id", + "name" + ], + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Bayern" + }, + "note": { + "type": "string", + "example": "Central region" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.FederalStateCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "federal_state_create", + "federal_state" + ], + "example": "federal_state_create" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateCreateData" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.FederalStateDeleteResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "federal_state_delete" + } + } + } + } + }, + "wucher_internal_transport_http_dto.FederalStateListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.FederalStateResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "federal_state" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateResource" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateUpdateAttributes": { + "type": "object", + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Bayern" + }, + "note": { + "type": "string", + "example": "Central region" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.FederalStateUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "federal_state_update", + "federal_state" + ], + "example": "federal_state_update" + } + } + }, + "wucher_internal_transport_http_dto.FederalStateUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FederalStateUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.FlightAssistantProfileAttributes": { + "type": "object", + "properties": { + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.FlightAssistantProfileUpdateAttributes": { + "type": "object", + "properties": { + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-04-09T05:00:00Z" + }, + "created_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "inspection_date": { + "type": "string", + "example": "2026-04-09" + }, + "status": { + "type": "string", + "example": "Draft" + }, + "updated_at": { + "type": "string", + "example": "2026-04-09T05:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionCreateAttributes": { + "type": "object", + "required": [ + "inspection_date" + ], + "properties": { + "inspection_date": { + "type": "string", + "example": "2026-04-09" + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "flight_inspection_create", + "flight_inspection" + ] + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionCreateData" + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionAttributes" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "type": { + "type": "string", + "example": "flight_inspection" + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionResource" + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionUpdateAttributes": { + "type": "object", + "properties": { + "inspection_date": { + "type": "string", + "example": "2026-04-09" + }, + "status": { + "type": "string", + "example": "InProgress" + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionUpdateAttributes" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "type": { + "type": "string", + "enum": [ + "flight_inspection_update", + "flight_inspection" + ] + } + } + }, + "wucher_internal_transport_http_dto.FlightInspectionUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.FlightInspectionUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "Firefighter Team A" + }, + "note": { + "type": "string", + "example": "On-site responders" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "Firefighter Team A" + }, + "note": { + "type": "string", + "example": "On-site responders" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "forces_present_create", + "forces_present" + ], + "example": "forces_present_create" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentCreateData" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "forces_present" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentResource" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentUpdateAttributes": { + "type": "object", + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "Firefighter Team A" + }, + "note": { + "type": "string", + "example": "On-site responders" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "forces_present_update", + "forces_present" + ], + "example": "forces_present_update" + } + } + }, + "wucher_internal_transport_http_dto.ForcesPresentUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForcesPresentUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ForgotPasswordAttributes": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + } + } + }, + "wucher_internal_transport_http_dto.ForgotPasswordData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForgotPasswordAttributes" + }, + "type": { + "type": "string", + "example": "auth_forgot_password" + } + } + }, + "wucher_internal_transport_http_dto.ForgotPasswordRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForgotPasswordData" + } + } + }, + "wucher_internal_transport_http_dto.ForgotSecurityPINAttributes": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + } + } + }, + "wucher_internal_transport_http_dto.ForgotSecurityPINData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForgotSecurityPINAttributes" + }, + "type": { + "type": "string", + "example": "auth_forgot_pin" + } + } + }, + "wucher_internal_transport_http_dto.ForgotSecurityPINRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ForgotSecurityPINData" + } + } + }, + "wucher_internal_transport_http_dto.GenericDeleteAttributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto.GenericDeleteResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteResponseData" + } + } + }, + "wucher_internal_transport_http_dto.GenericDeleteResponseData": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.GenericDeleteAttributes" + }, + "type": { + "type": "string", + "example": "delete_result" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "date": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "hems_operation_category": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryRef" + }, + "hems_operation_category_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mission": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationMissionRef" + }, + "mission_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "operational_data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationOperationalDataRef" + }, + "operational_data_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Firefighting" + }, + "type": { + "type": "string", + "example": "first" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryCreateAttributes": { + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "type": "string", + "example": "Firefighting" + }, + "type": { + "type": "string", + "enum": [ + "first", + "secondary", + "misuse" + ], + "example": "first" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hems_operation_category_create", + "hems_operation_category" + ], + "example": "hems_operation_category_create" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryCreateData" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryRef": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Firefighting" + }, + "type": { + "type": "string", + "example": "first" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "hems_operation_category" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryResource" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryUpdateAttributes": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Firefighting" + }, + "type": { + "type": "string", + "enum": [ + "first", + "secondary", + "misuse" + ], + "example": "first" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryUpdateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hems_operation_category_update", + "hems_operation_category" + ], + "example": "hems_operation_category_update" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCategoryUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCategoryUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCreateAttributes": { + "type": "object", + "required": [ + "date" + ], + "properties": { + "date": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "hems_operation_category_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mission_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "operational_data_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hems_operation_create", + "hems_operation" + ], + "example": "hems_operation_create" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationCreateData" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationMissionRef": { + "type": "object", + "properties": { + "flight_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationOperationalDataRef": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "hems_operation" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationResource" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationUpdateAttributes": { + "type": "object", + "properties": { + "date": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "hems_operation_category_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mission_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "operational_data_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationUpdateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hems_operation_update", + "hems_operation" + ], + "example": "hems_operation_update" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataAttributes": { + "type": "object", + "properties": { + "additional_info": { + "type": "string", + "example": "Additional information about the operational data" + }, + "altitude": { + "type": "number", + "example": 5.2 + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "darkness": { + "type": "boolean" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "false_information": { + "type": "boolean" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataFileAttributes" + } + }, + "fog": { + "type": "boolean" + }, + "force_present_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + ] + }, + "instrument_flight": { + "type": "boolean" + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "landing_site_search": { + "type": "boolean" + }, + "location": { + "type": "string", + "example": "123 Main St, Anytown, USA" + }, + "mountain_use": { + "type": "boolean" + }, + "nfo_search": { + "type": "boolean" + }, + "night_flight": { + "type": "boolean" + }, + "notes": { + "type": "string", + "example": "Notes about the operational data" + }, + "postcode": { + "type": "string", + "example": "12345" + }, + "precipitation": { + "type": "boolean" + }, + "rope_recovery": { + "type": "boolean" + }, + "state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "temperature": { + "type": "number", + "example": 23.5 + }, + "terrain_index": { + "type": "string", + "example": "Only helicopter landing" + }, + "time": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "vocation_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "wind": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataCreateAttributes": { + "type": "object", + "required": [ + "location", + "time" + ], + "properties": { + "additional_info": { + "type": "string", + "example": "Additional information about the operational data" + }, + "altitude": { + "type": "number", + "example": 5.2 + }, + "darkness": { + "type": "boolean" + }, + "false_information": { + "type": "boolean" + }, + "file_attachment_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + ] + }, + "fog": { + "type": "boolean" + }, + "force_present_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + ] + }, + "instrument_flight": { + "type": "boolean" + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "landing_site_search": { + "type": "boolean" + }, + "location": { + "type": "string", + "example": "123 Main St, Anytown, USA" + }, + "mountain_use": { + "type": "boolean" + }, + "nfo_search": { + "type": "boolean" + }, + "night_flight": { + "type": "boolean" + }, + "notes": { + "type": "string", + "example": "Notes about the operational data" + }, + "postcode": { + "type": "string", + "example": "12345" + }, + "precipitation": { + "type": "boolean" + }, + "rope_recovery": { + "type": "boolean" + }, + "state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "temperature": { + "type": "number", + "example": 23.5 + }, + "terrain_index": { + "type": "string", + "example": "Only helicopter landing" + }, + "time": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "vocation_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "wind": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hems_operational_data_create", + "hems_operational_data" + ], + "example": "hems_operational_data_create" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataCreateData" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataFileAttributes": { + "type": "object", + "properties": { + "file_attachment_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "hems_operational_data" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataResource" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataUpdateAttributes": { + "type": "object", + "properties": { + "additional_info": { + "type": "string", + "example": "Additional information about the operational data" + }, + "altitude": { + "type": "number", + "example": 5.2 + }, + "darkness": { + "type": "boolean" + }, + "false_information": { + "type": "boolean" + }, + "file_attachment_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "fog": { + "type": "boolean" + }, + "force_present_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "instrument_flight": { + "type": "boolean" + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "landing_site_search": { + "type": "boolean" + }, + "location": { + "type": "string", + "example": "123 Main St, Anytown, USA" + }, + "mountain_use": { + "type": "boolean" + }, + "nfo_search": { + "type": "boolean" + }, + "night_flight": { + "type": "boolean" + }, + "notes": { + "type": "string", + "example": "Notes about the operational data" + }, + "postcode": { + "type": "string", + "example": "12345" + }, + "precipitation": { + "type": "boolean" + }, + "rope_recovery": { + "type": "boolean" + }, + "state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "temperature": { + "type": "number", + "example": 23.5 + }, + "terrain_index": { + "type": "string", + "example": "Only helicopter landing" + }, + "time": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "vocation_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "wind": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataUpdateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hems_operational_data_update", + "hems_operational_data" + ], + "example": "hems_operational_data_update" + } + } + }, + "wucher_internal_transport_http_dto.HEMSOperationalDataUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HEMSOperationalDataUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Main Street 10" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "email": { + "type": "string", + "example": "info@abc-insurance.local" + }, + "federal_state": { + "type": "string", + "example": "Bayern" + }, + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "land_iso_code": { + "type": "string", + "example": "DE" + }, + "land_name": { + "type": "string", + "example": "Germany" + }, + "mobile_number": { + "type": "string", + "example": "+4912345678" + }, + "name": { + "type": "string", + "example": "ABC Insurance" + }, + "note": { + "type": "string", + "example": "Preferred insurance provider" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "state": { + "type": "string", + "example": "Berlin" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "address": { + "type": "string", + "example": "Main Street 10" + }, + "email": { + "type": "string", + "example": "info@abc-insurance.local" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mobile_number": { + "type": "string", + "example": "+4912345678" + }, + "name": { + "type": "string", + "example": "ABC Insurance" + }, + "note": { + "type": "string", + "example": "Preferred insurance provider" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "state": { + "type": "string", + "example": "Berlin" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "health_insurance_company_create", + "health_insurance_company" + ], + "example": "health_insurance_company_create" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyCreateData" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "health_insurance_company" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyResource" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyUpdateAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Main Street 10" + }, + "email": { + "type": "string", + "example": "info@abc-insurance.local" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mobile_number": { + "type": "string", + "example": "+4912345678" + }, + "name": { + "type": "string", + "example": "ABC Insurance" + }, + "note": { + "type": "string", + "example": "Preferred insurance provider" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "state": { + "type": "string", + "example": "Berlin" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "health_insurance_company_update", + "health_insurance_company" + ], + "example": "health_insurance_company_update" + } + } + }, + "wucher_internal_transport_http_dto.HealthInsuranceCompanyUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HealthInsuranceCompanyUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "helicopter_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "total_airframe_cycles": { + "type": "number", + "example": 320 + }, + "total_airframe_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_1_ccc": { + "type": "number", + "example": 44 + }, + "total_engine_1_gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "total_engine_1_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_1_ptc_nf_n2": { + "type": "number", + "example": 97.2 + }, + "total_engine_2_ccc": { + "type": "number", + "example": 44 + }, + "total_engine_2_gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "total_engine_2_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_2_ptc_nf_n2": { + "type": "number", + "example": 97.2 + }, + "total_flight_report": { + "type": "number", + "example": 80 + }, + "total_hook_release": { + "type": "number", + "example": 10 + }, + "total_landing": { + "type": "number", + "example": 120 + }, + "total_rotor_brake_cycle": { + "type": "number", + "example": 8 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageCreateAttributes": { + "type": "object", + "required": [ + "helicopter_id" + ], + "properties": { + "helicopter_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "total_airframe_cycles": { + "type": "number", + "example": 320 + }, + "total_airframe_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_1_ccc": { + "type": "number", + "example": 44 + }, + "total_engine_1_gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "total_engine_1_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_1_ptc_nf_n2": { + "type": "number", + "example": 97.2 + }, + "total_engine_2_ccc": { + "type": "number", + "example": 44 + }, + "total_engine_2_gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "total_engine_2_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_2_ptc_nf_n2": { + "type": "number", + "example": 97.2 + }, + "total_flight_report": { + "type": "number", + "example": 80 + }, + "total_hook_release": { + "type": "number", + "example": 10 + }, + "total_landing": { + "type": "number", + "example": 120 + }, + "total_rotor_brake_cycle": { + "type": "number", + "example": 8 + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_usage_create", + "helicopter_usage" + ], + "example": "helicopter_usage_create" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageCreateData" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "helicopter_usage" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageResource" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageUpdateAttributes": { + "type": "object", + "properties": { + "helicopter_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "total_airframe_cycles": { + "type": "number", + "example": 320 + }, + "total_airframe_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_1_ccc": { + "type": "number", + "example": 44 + }, + "total_engine_1_gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "total_engine_1_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_1_ptc_nf_n2": { + "type": "number", + "example": 97.2 + }, + "total_engine_2_ccc": { + "type": "number", + "example": 44 + }, + "total_engine_2_gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "total_engine_2_hours": { + "type": "number", + "example": 1523.5 + }, + "total_engine_2_ptc_nf_n2": { + "type": "number", + "example": 97.2 + }, + "total_flight_report": { + "type": "number", + "example": 80 + }, + "total_hook_release": { + "type": "number", + "example": 10 + }, + "total_landing": { + "type": "number", + "example": 120 + }, + "total_rotor_brake_cycle": { + "type": "number", + "example": 8 + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_usage_update", + "helicopter_usage" + ], + "example": "helicopter_usage_update" + } + } + }, + "wucher_internal_transport_http_dto.HelicopterUsageUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HelicopterUsageUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.HospitalAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Jl. Merdeka No. 10" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "email": { + "type": "string", + "example": "info@hospital.local" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "landline_number": { + "type": "string", + "example": "0211234567" + }, + "mobile_number": { + "type": "string", + "example": "08123456789" + }, + "name": { + "type": "string", + "example": "RSUD Kota" + }, + "note": { + "type": "string", + "example": "Nearest trauma center" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.HospitalCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "address": { + "type": "string", + "example": "Jl. Merdeka No. 10" + }, + "email": { + "type": "string", + "example": "info@hospital.local" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "landline_number": { + "type": "string", + "example": "0211234567" + }, + "mobile_number": { + "type": "string", + "example": "08123456789" + }, + "name": { + "type": "string", + "example": "RSUD Kota" + }, + "note": { + "type": "string", + "example": "Nearest trauma center" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.HospitalCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "hospital_create", + "hospital" + ], + "example": "hospital_create" + } + } + }, + "wucher_internal_transport_http_dto.HospitalCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalCreateData" + } + } + }, + "wucher_internal_transport_http_dto.HospitalDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HospitalListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.HospitalResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "hospital" + } + } + }, + "wucher_internal_transport_http_dto.HospitalResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalResource" + } + } + }, + "wucher_internal_transport_http_dto.HospitalUpdateAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Jl. Merdeka No. 10" + }, + "email": { + "type": "string", + "example": "info@hospital.local" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "landline_number": { + "type": "string", + "example": "0211234567" + }, + "mobile_number": { + "type": "string", + "example": "08123456789" + }, + "name": { + "type": "string", + "example": "RSUD Kota" + }, + "note": { + "type": "string", + "example": "Nearest trauma center" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.HospitalUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "hospital_update", + "hospital" + ], + "example": "hospital_update" + } + } + }, + "wucher_internal_transport_http_dto.HospitalUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.HospitalUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.ICAOAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Muehlenstrasse 1" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "email": { + "type": "string", + "example": "ops@example.local" + }, + "federal_state": { + "type": "string", + "example": "Bayern" + }, + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "icao_code": { + "type": "string", + "example": "EDDB" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "land_iso_code": { + "type": "string", + "example": "DE" + }, + "land_name": { + "type": "string", + "example": "Germany" + }, + "landline_number": { + "type": "string", + "example": "030123456" + }, + "mobile_number": { + "type": "string", + "example": "0170123456" + }, + "name": { + "type": "string", + "example": "Berlin Base" + }, + "note": { + "type": "string", + "example": "Night operations supported" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.ICAOCreateAttributes": { + "type": "object", + "required": [ + "icao_code" + ], + "properties": { + "address": { + "type": "string", + "example": "Muehlenstrasse 1" + }, + "email": { + "type": "string", + "example": "ops@example.local" + }, + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "icao_code": { + "type": "string", + "example": "EDDB" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "landline_number": { + "type": "string", + "example": "030123456" + }, + "mobile_number": { + "type": "string", + "example": "0170123456" + }, + "name": { + "type": "string", + "example": "Berlin Base" + }, + "note": { + "type": "string", + "example": "Night operations supported" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.ICAOCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "icao_create", + "icao" + ], + "example": "icao_create" + } + } + }, + "wucher_internal_transport_http_dto.ICAOCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOCreateData" + } + } + }, + "wucher_internal_transport_http_dto.ICAODataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.ICAOListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.ICAOResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "icao" + } + } + }, + "wucher_internal_transport_http_dto.ICAOResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOResource" + } + } + }, + "wucher_internal_transport_http_dto.ICAOUpdateAttributes": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Muehlenstrasse 1" + }, + "email": { + "type": "string", + "example": "ops@example.local" + }, + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "icao_code": { + "type": "string", + "example": "EDDB" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "land_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "landline_number": { + "type": "string", + "example": "030123456" + }, + "mobile_number": { + "type": "string", + "example": "0170123456" + }, + "name": { + "type": "string", + "example": "Berlin Base" + }, + "note": { + "type": "string", + "example": "Night operations supported" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.ICAOUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "icao_update", + "icao" + ], + "example": "icao_update" + } + } + }, + "wucher_internal_transport_http_dto.ICAOUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ICAOUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "federal_state": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataFederalState" + }, + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "health_insurance_companies_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "health_insurance_company": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataHealthInsuranceCompany" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataCreateAttributes": { + "type": "object", + "required": [ + "federal_state_id", + "health_insurance_companies_id" + ], + "properties": { + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "health_insurance_companies_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "insurance_patient_data_create", + "insurance_patient_data" + ], + "example": "insurance_patient_data_create" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataCreateData" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataFederalState": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Bayern" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataHealthInsuranceCompany": { + "type": "object", + "properties": { + "address": { + "type": "string", + "example": "Main Street 10" + }, + "email": { + "type": "string", + "example": "info@abc-insurance.local" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mobile_number": { + "type": "string", + "example": "+4912345678" + }, + "name": { + "type": "string", + "example": "ABC Insurance" + }, + "state": { + "type": "string", + "example": "Berlin" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "insurance_patient_data" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataResource" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataUpdateAttributes": { + "type": "object", + "properties": { + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "health_insurance_companies_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "insurance_patient_data_update", + "insurance_patient_data" + ], + "example": "insurance_patient_data_update" + } + } + }, + "wucher_internal_transport_http_dto.InsurancePatientDataUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.InviteAttributes": { + "type": "object", + "required": [ + "email", + "first_name", + "last_name", + "role_id", + "username" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "last_name": { + "type": "string", + "example": "Doe" + }, + "mobile_phone": { + "type": "string", + "example": "+628123456789" + }, + "role_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "role_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d\"", + "\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"]" + ] + }, + "username": { + "type": "string", + "maxLength": 100, + "minLength": 3, + "example": "john.doe" + } + } + }, + "wucher_internal_transport_http_dto.InviteData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InviteAttributes" + }, + "type": { + "type": "string", + "example": "auth_invite" + } + } + }, + "wucher_internal_transport_http_dto.InviteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InviteData" + } + } + }, + "wucher_internal_transport_http_dto.LandAttributes": { + "type": "object", + "properties": { + "bmd_export_id": { + "type": "string", + "example": "DE001" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "federal_state_list": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandFederalStateDTO" + } + }, + "federal_state_total": { + "type": "integer", + "example": 2 + }, + "is_active": { + "type": "boolean", + "example": true + }, + "iso_code": { + "type": "string", + "example": "DE" + }, + "name": { + "type": "string", + "example": "Germany" + }, + "note": { + "type": "string", + "example": "Central Europe" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.LandCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "bmd_export_id": { + "type": "string", + "example": "DE001" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "iso_code": { + "type": "string", + "example": "DE" + }, + "name": { + "type": "string", + "example": "Germany" + }, + "note": { + "type": "string", + "example": "Central Europe" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.LandCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "land_create", + "land" + ], + "example": "land_create" + } + } + }, + "wucher_internal_transport_http_dto.LandCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandCreateData" + } + } + }, + "wucher_internal_transport_http_dto.LandDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.LandFederalStateDTO": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Bayern" + } + } + }, + "wucher_internal_transport_http_dto.LandListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.LandResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "land" + } + } + }, + "wucher_internal_transport_http_dto.LandResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandResource" + } + } + }, + "wucher_internal_transport_http_dto.LandUpdateAttributes": { + "type": "object", + "properties": { + "bmd_export_id": { + "type": "string", + "example": "DE001" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "iso_code": { + "type": "string", + "example": "DE" + }, + "name": { + "type": "string", + "example": "Germany" + }, + "note": { + "type": "string", + "example": "Central Europe" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.LandUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "land_update", + "land" + ], + "example": "land_update" + } + } + }, + "wucher_internal_transport_http_dto.LandUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LandUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.LoginAttributes": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "password": { + "type": "string", + "example": "StrongP@ssw0rd" + } + } + }, + "wucher_internal_transport_http_dto.LoginData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LoginAttributes" + }, + "type": { + "type": "string", + "example": "auth_login" + } + } + }, + "wucher_internal_transport_http_dto.LoginEmailOTPRequiredResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "linked_sso": { + "type": "boolean", + "example": false + }, + "max_resend_per_hour": { + "type": "integer", + "example": 5 + }, + "pin_configured": { + "type": "boolean", + "example": false + }, + "requires_email_otp": { + "type": "boolean", + "example": true + }, + "requires_totp_setup": { + "type": "boolean", + "example": true + }, + "resend_cooldown_seconds": { + "type": "integer", + "example": 60 + }, + "webauthn_configured": { + "type": "boolean", + "example": false + } + } + }, + "type": { + "type": "string", + "example": "auth_email_otp_required" + } + } + } + } + }, + "wucher_internal_transport_http_dto.LoginRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.LoginData" + } + } + }, + "wucher_internal_transport_http_dto.LoginTOTPRequiredResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "email_otp_challenge_token": { + "type": "string", + "example": "qA9j1..." + }, + "email_otp_enabled": { + "type": "boolean", + "example": false + }, + "email_otp_max_resend_per_hour": { + "type": "integer", + "example": 5 + }, + "email_otp_resend_cooldown_seconds": { + "type": "integer", + "example": 60 + }, + "linked_sso": { + "type": "boolean", + "example": false + }, + "pin_configured": { + "type": "boolean", + "example": false + }, + "requires_totp": { + "type": "boolean", + "example": true + }, + "totp_enabled": { + "type": "boolean", + "example": true + }, + "webauthn_configured": { + "type": "boolean", + "example": false + } + } + }, + "type": { + "type": "string", + "example": "auth_totp_required" + } + } + } + } + }, + "wucher_internal_transport_http_dto.MedicineAttributes": { + "type": "object", + "properties": { + "column": { + "type": "integer", + "example": 1 + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "medicine_group": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupResource" + }, + "medicine_group_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "motor_reaction": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResource" + }, + "motor_reaction_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "motor_reaction_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + ] + }, + "motor_reactions": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResource" + } + }, + "name": { + "type": "string", + "example": "Paracetamol" + }, + "note": { + "type": "string", + "example": "Main stock medicine" + }, + "pack": { + "type": "string", + "example": "500mg" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "unit": { + "type": "string", + "example": "tablet" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.MedicineCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "column": { + "type": "integer", + "example": 1 + }, + "is_active": { + "type": "boolean", + "example": true + }, + "medicine_group_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "motor_reaction_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "motor_reaction_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + ] + }, + "name": { + "type": "string", + "example": "Paracetamol" + }, + "note": { + "type": "string", + "example": "Main stock medicine" + }, + "pack": { + "type": "string", + "example": "500mg" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "unit": { + "type": "string", + "maxLength": 50, + "example": "tablet" + } + } + }, + "wucher_internal_transport_http_dto.MedicineCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "medicine_create", + "medicine" + ], + "example": "medicine_create" + } + } + }, + "wucher_internal_transport_http_dto.MedicineCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineCreateData" + } + } + }, + "wucher_internal_transport_http_dto.MedicineDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Spritze" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "example": "Spritze" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "medicine_group_create", + "medicine_group" + ], + "example": "medicine_group_create" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupCreateData" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "medicine_group" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupResource" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupUpdateAttributes": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Spritze" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "medicine_group_update", + "medicine_group" + ], + "example": "medicine_group_update" + } + } + }, + "wucher_internal_transport_http_dto.MedicineGroupUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineGroupUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.MedicineListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.MedicineResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "medicine" + } + } + }, + "wucher_internal_transport_http_dto.MedicineResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineResource" + } + } + }, + "wucher_internal_transport_http_dto.MedicineUpdateAttributes": { + "type": "object", + "properties": { + "column": { + "type": "integer", + "example": 1 + }, + "is_active": { + "type": "boolean", + "example": true + }, + "medicine_group_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "motor_reaction_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "motor_reaction_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + ] + }, + "name": { + "type": "string", + "example": "Paracetamol" + }, + "note": { + "type": "string", + "example": "Main stock medicine" + }, + "pack": { + "type": "string", + "example": "500mg" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "unit": { + "type": "string", + "maxLength": 50, + "example": "tablet" + } + } + }, + "wucher_internal_transport_http_dto.MedicineUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "medicine_update", + "medicine" + ], + "example": "medicine_update" + } + } + }, + "wucher_internal_transport_http_dto.MedicineUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MedicineUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.MicrosoftCallbackResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "provider": { + "type": "string", + "example": "microsoft" + }, + "provider_subject": { + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + } + } + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "auth_identity" + } + } + } + } + }, + "wucher_internal_transport_http_dto.MicrosoftLoginResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "redirect_url": { + "type": "string", + "example": "https://login.microsoftonline.com/..." + }, + "url": { + "type": "string", + "example": "https://login.microsoftonline.com/..." + } + } + }, + "type": { + "type": "string", + "example": "auth_microsoft_url" + } + } + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "gezielt auf Schmerzreiz" + }, + "score": { + "type": "integer", + "example": 5 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "example": "gezielt auf Schmerzreiz" + }, + "score": { + "type": "integer", + "example": 5 + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "motor_reaction_create", + "motor_reaction" + ], + "example": "motor_reaction_create" + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionCreateData" + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "motor_reaction" + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionResource" + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionUpdateAttributes": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "gezielt auf Schmerzreiz" + }, + "score": { + "type": "integer", + "example": 5 + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "motor_reaction_update", + "motor_reaction" + ], + "example": "motor_reaction_update" + } + } + }, + "wucher_internal_transport_http_dto.MotorReactionUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.MotorReactionUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.OpcAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "note": { + "type": "string", + "example": "Primary operation control center" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "title": { + "type": "string", + "example": "Main OPC Base" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.OpcCreateAttributes": { + "type": "object", + "required": [ + "title" + ], + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "note": { + "type": "string", + "example": "Primary operation control center" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "title": { + "type": "string", + "example": "Main OPC Base" + } + } + }, + "wucher_internal_transport_http_dto.OpcCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "opc_create", + "opc" + ], + "example": "opc_create" + } + } + }, + "wucher_internal_transport_http_dto.OpcCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcCreateData" + } + } + }, + "wucher_internal_transport_http_dto.OpcDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.OpcListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.OpcResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "opc" + } + } + }, + "wucher_internal_transport_http_dto.OpcResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcResource" + } + } + }, + "wucher_internal_transport_http_dto.OpcUpdateAttributes": { + "type": "object", + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "note": { + "type": "string", + "example": "Primary operation control center" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "title": { + "type": "string", + "example": "Main OPC Base" + } + } + }, + "wucher_internal_transport_http_dto.OpcUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "opc_update", + "opc" + ], + "example": "opc_update" + } + } + }, + "wucher_internal_transport_http_dto.OpcUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.OpcUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataAttributes": { + "type": "object", + "properties": { + "age": { + "type": "integer", + "example": 35 + }, + "birth_date": { + "type": "string", + "example": "1990-01-15" + }, + "co_insured": { + "type": "boolean", + "example": false + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "email": { + "type": "string", + "example": "max.muster@example.com" + }, + "family_name": { + "type": "string", + "example": "Muster" + }, + "first_name": { + "type": "string", + "example": "Max" + }, + "gender": { + "type": "string", + "example": "male" + }, + "insurance_patient_data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataInsuranceDetail" + }, + "insurance_patient_data_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "opc": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataOPC" + }, + "opc_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "phone": { + "type": "string", + "example": "+431234567" + }, + "street": { + "type": "string", + "example": "Hauptstraße 1" + }, + "svnr": { + "type": "string", + "example": "1234150190" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataCreateAttributes": { + "type": "object", + "required": [ + "family_name", + "first_name", + "gender" + ], + "properties": { + "birth_date": { + "type": "string", + "example": "1990-01-15" + }, + "co_insured": { + "type": "boolean", + "example": false + }, + "email": { + "type": "string", + "example": "max.muster@example.com" + }, + "family_name": { + "type": "string", + "example": "Muster" + }, + "first_name": { + "type": "string", + "example": "Max" + }, + "gender": { + "type": "string", + "enum": [ + "male", + "female" + ], + "example": "male" + }, + "insurance_patient_data_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "opc_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "phone": { + "type": "string", + "example": "+431234567" + }, + "street": { + "type": "string", + "example": "Hauptstraße 1" + }, + "svnr": { + "type": "string", + "example": "1234150190" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "patient_data_create", + "patient_data" + ], + "example": "patient_data_create" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataCreateData" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.PatientDataInsuranceDetail": { + "type": "object", + "properties": { + "federal_state": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataFederalState" + }, + "federal_state_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "health_insurance_companies_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "health_insurance_company": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.InsurancePatientDataHealthInsuranceCompany" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.PatientDataOPC": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "title": { + "type": "string", + "example": "Primary OPC" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "patient_data" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataResource" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataUpdateAttributes": { + "type": "object", + "properties": { + "birth_date": { + "type": "string", + "example": "1990-01-15" + }, + "co_insured": { + "type": "boolean", + "example": false + }, + "email": { + "type": "string", + "example": "max.muster@example.com" + }, + "family_name": { + "type": "string", + "example": "Muster" + }, + "first_name": { + "type": "string", + "example": "Max" + }, + "gender": { + "type": "string", + "example": "male" + }, + "insurance_patient_data_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "opc_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "phone": { + "type": "string", + "example": "+431234567" + }, + "street": { + "type": "string", + "example": "Hauptstraße 1" + }, + "svnr": { + "type": "string", + "example": "1234150190" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "patient_data_update", + "patient_data" + ], + "example": "patient_data_update" + } + } + }, + "wucher_internal_transport_http_dto.PatientDataUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PatientDataUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.PermissionAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-02-05T10:00:00Z" + }, + "description": { + "type": "string", + "example": "Allows managing users." + }, + "key": { + "type": "string", + "example": "user.manage" + }, + "name": { + "type": "string", + "example": "User Management" + }, + "requires_pin": { + "type": "boolean", + "example": true + }, + "updated_at": { + "type": "string", + "example": "2026-02-05T10:00:00Z" + } + } + }, + "wucher_internal_transport_http_dto.PermissionGroupResource": { + "type": "object", + "properties": { + "module": { + "type": "string", + "example": "dul" + }, + "module_label": { + "type": "string", + "example": "DUL" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionResource" + } + } + } + }, + "wucher_internal_transport_http_dto.PermissionGroupedResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionGroupResource" + } + } + } + }, + "wucher_internal_transport_http_dto.PermissionListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.PermissionMini": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "Permission to view users." + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9a" + }, + "key": { + "type": "string", + "example": "users.read" + }, + "name": { + "type": "string", + "example": "View Users" + }, + "requires_pin": { + "type": "boolean", + "example": false + } + } + }, + "wucher_internal_transport_http_dto.PermissionResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "permission" + } + } + }, + "wucher_internal_transport_http_dto.PermissionUpdateAttributes": { + "type": "object", + "required": [ + "requires_pin" + ], + "properties": { + "requires_pin": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto.PermissionUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "permission_update", + "permission" + ], + "example": "permission_update" + } + } + }, + "wucher_internal_transport_http_dto.PermissionUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.PilotProfileAttributes": { + "type": "object", + "properties": { + "double_command_minutes": { + "type": "integer" + }, + "dul_base": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULBase" + } + }, + "flight_instructor_minutes": { + "type": "integer" + }, + "has_ifr_qualification": { + "type": "boolean" + }, + "ifr_flight_minutes": { + "type": "integer" + }, + "is_chief_pilot": { + "type": "boolean" + }, + "license_expired_at": { + "type": "string" + }, + "license_issued_at": { + "type": "string" + }, + "license_no": { + "type": "string", + "maxLength": 120 + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "night_flight_minutes": { + "type": "integer" + }, + "note": { + "type": "string" + }, + "pilot_category": { + "type": "string", + "enum": [ + "regular", + "freelance", + "dry_lease" + ] + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "responsible_pilot_minutes": { + "type": "integer" + }, + "second_pilot_minutes": { + "type": "integer" + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + }, + "technician_license_no": { + "type": "string", + "maxLength": 120 + }, + "total_flight_minutes": { + "type": "integer" + }, + "weight_kg": { + "type": "number" + } + } + }, + "wucher_internal_transport_http_dto.PilotProfileUpdateAttributes": { + "type": "object", + "properties": { + "double_command_minutes": { + "type": "integer" + }, + "dul_base": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.DULBase" + } + }, + "flight_instructor_minutes": { + "type": "integer" + }, + "has_ifr_qualification": { + "type": "boolean" + }, + "ifr_flight_minutes": { + "type": "integer" + }, + "is_chief_pilot": { + "type": "boolean" + }, + "license_expired_at": { + "type": "string" + }, + "license_issued_at": { + "type": "string" + }, + "license_no": { + "type": "string", + "maxLength": 120 + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "night_flight_minutes": { + "type": "integer" + }, + "note": { + "type": "string" + }, + "pilot_category": { + "type": "string", + "enum": [ + "regular", + "freelance", + "dry_lease" + ] + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "responsible_pilot_minutes": { + "type": "integer" + }, + "second_pilot_minutes": { + "type": "integer" + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + }, + "technician_license_no": { + "type": "string", + "maxLength": 120 + }, + "total_flight_minutes": { + "type": "integer" + }, + "weight_kg": { + "type": "number" + } + } + }, + "wucher_internal_transport_http_dto.ReauthAttributes": { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "pin", + "password" + ], + "example": "pin" + }, + "password": { + "type": "string", + "minLength": 1, + "example": "StrongP@ssw0rd" + } + } + }, + "wucher_internal_transport_http_dto.RegisterAttributes": { + "type": "object", + "required": [ + "email", + "first_name", + "last_name", + "password", + "username" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "last_name": { + "type": "string", + "example": "Doe" + }, + "mobile_phone": { + "type": "string", + "example": "+628123456789" + }, + "password": { + "type": "string", + "minLength": 8, + "example": "StrongP@ssw0rd" + }, + "username": { + "type": "string", + "maxLength": 100, + "minLength": 3, + "example": "john.doe" + } + } + }, + "wucher_internal_transport_http_dto.RegisterData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RegisterAttributes" + }, + "type": { + "type": "string", + "example": "auth_register" + } + } + }, + "wucher_internal_transport_http_dto.RegisterRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RegisterData" + } + } + }, + "wucher_internal_transport_http_dto.RequestSecurityPINAttributes": { + "type": "object", + "properties": { + "method": { + "type": "string", + "example": "password" + }, + "password": { + "type": "string", + "example": "StrongP@ssw0rd" + }, + "reauth_token": { + "type": "string", + "example": "sso_reauth_1234567890abcdef" + } + } + }, + "wucher_internal_transport_http_dto.RequestSecurityPINData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RequestSecurityPINAttributes" + }, + "type": { + "type": "string", + "example": "auth_pin_request_reset" + } + } + }, + "wucher_internal_transport_http_dto.RequestSecurityPINRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RequestSecurityPINData" + } + } + }, + "wucher_internal_transport_http_dto.RequestSecurityPINResetResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "If allowed, a security PIN reset link has been sent to your email." + } + } + }, + "type": { + "type": "string", + "example": "auth_pin_request_reset" + } + } + } + } + }, + "wucher_internal_transport_http_dto.ResendSetPasswordInviteAttributes": { + "type": "object", + "required": [ + "user_id" + ], + "properties": { + "user_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.ResendSetPasswordInviteData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResendSetPasswordInviteAttributes" + }, + "type": { + "type": "string", + "example": "auth_resend_set_password" + } + } + }, + "wucher_internal_transport_http_dto.ResendSetPasswordInviteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResendSetPasswordInviteData" + } + } + }, + "wucher_internal_transport_http_dto.ResetPasswordAttributes": { + "type": "object", + "required": [ + "confirm_password", + "new_password", + "token" + ], + "properties": { + "confirm_password": { + "type": "string", + "minLength": 8, + "example": "StrongP@ssw0rd!LongPassphrase" + }, + "new_password": { + "type": "string", + "minLength": 8, + "example": "StrongP@ssw0rd!LongPassphrase" + }, + "token": { + "type": "string", + "example": "kP8k2..." + } + } + }, + "wucher_internal_transport_http_dto.ResetPasswordData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResetPasswordAttributes" + }, + "type": { + "type": "string", + "example": "auth_reset_password" + } + } + }, + "wucher_internal_transport_http_dto.ResetPasswordRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResetPasswordData" + } + } + }, + "wucher_internal_transport_http_dto.ResetSecurityPINAttributes": { + "type": "object", + "required": [ + "confirm_pin", + "new_pin" + ], + "properties": { + "confirm_pin": { + "type": "string", + "example": "123456" + }, + "method": { + "type": "string", + "example": "password" + }, + "new_pin": { + "type": "string", + "example": "123456" + }, + "password": { + "type": "string", + "example": "StrongP@ssw0rd" + }, + "reauth_token": { + "type": "string", + "example": "sso_reauth_1234567890abcdef" + }, + "token": { + "type": "string", + "example": "kP8k2..." + } + } + }, + "wucher_internal_transport_http_dto.ResetSecurityPINData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResetSecurityPINAttributes" + }, + "type": { + "type": "string", + "example": "auth_reset_pin" + } + } + }, + "wucher_internal_transport_http_dto.ResetSecurityPINRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ResetSecurityPINData" + } + } + }, + "wucher_internal_transport_http_dto.RoleAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-02-05T10:00:00Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "description": { + "type": "string", + "example": "Full access to all resources." + }, + "name": { + "type": "string", + "example": "admin" + }, + "updated_at": { + "type": "string", + "example": "2026-02-05T10:00:00Z" + } + } + }, + "wucher_internal_transport_http_dto.RoleCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string", + "example": "Full access to all resources." + }, + "name": { + "type": "string", + "example": "admin" + } + } + }, + "wucher_internal_transport_http_dto.RoleCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "role_create", + "role" + ], + "example": "role_create" + } + } + }, + "wucher_internal_transport_http_dto.RoleCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleCreateData" + } + } + }, + "wucher_internal_transport_http_dto.RoleDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.RoleListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.RoleMini": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "Full access to all resources." + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "admin" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionAssignAttributes": { + "type": "object", + "required": [ + "permission_id" + ], + "properties": { + "permission_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionAssignBulkData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionBulkAttributes" + }, + "type": { + "type": "string", + "example": "role_assign_permission_bulk" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionAssignBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionAssignBulkData" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionAssignData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionAssignAttributes" + }, + "type": { + "type": "string", + "example": "role_permission" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionAssignRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionAssignData" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionAttributes": { + "type": "object", + "properties": { + "permission_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e" + }, + "role_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionBulkAttributes": { + "type": "object", + "required": [ + "permission_ids" + ], + "properties": { + "permission_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"", + "\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f\"]" + ] + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionResource" + } + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionRemoveBulkData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionBulkAttributes" + }, + "type": { + "type": "string", + "example": "role_remove_permission_bulk" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionRemoveBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionRemoveBulkData" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f" + }, + "type": { + "type": "string", + "example": "role_permission" + } + } + }, + "wucher_internal_transport_http_dto.RolePermissionResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RolePermissionResource" + } + } + }, + "wucher_internal_transport_http_dto.RoleResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.PermissionResource" + } + }, + "type": { + "type": "string", + "example": "role" + } + } + }, + "wucher_internal_transport_http_dto.RoleResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleResource" + } + } + }, + "wucher_internal_transport_http_dto.RoleUpdateAttributes": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "Full access to all resources." + }, + "name": { + "type": "string", + "example": "admin" + } + } + }, + "wucher_internal_transport_http_dto.RoleUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "role_update", + "role" + ], + "example": "role_update" + } + } + }, + "wucher_internal_transport_http_dto.RoleUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.SSOLinkAttributes": { + "type": "object", + "required": [ + "code", + "provider" + ], + "properties": { + "code": { + "type": "string", + "example": "0.AXkA..." + }, + "provider": { + "type": "string", + "example": "microsoft" + } + } + }, + "wucher_internal_transport_http_dto.SSOLinkData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOLinkAttributes" + }, + "type": { + "type": "string", + "example": "auth_sso_link" + } + } + }, + "wucher_internal_transport_http_dto.SSOLinkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOLinkData" + } + } + }, + "wucher_internal_transport_http_dto.SSOLinkResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "provider": { + "type": "string", + "example": "microsoft" + }, + "provider_subject": { + "type": "string", + "example": "00000000-0000-0000-0000-000000000000" + } + } + }, + "type": { + "type": "string", + "example": "auth_sso_link" + } + } + } + } + }, + "wucher_internal_transport_http_dto.SSOUnlinkAttributes": { + "type": "object", + "required": [ + "provider" + ], + "properties": { + "provider": { + "type": "string", + "example": "microsoft" + } + } + }, + "wucher_internal_transport_http_dto.SSOUnlinkData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOUnlinkAttributes" + }, + "type": { + "type": "string", + "example": "auth_sso_unlink" + } + } + }, + "wucher_internal_transport_http_dto.SSOUnlinkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SSOUnlinkData" + } + } + }, + "wucher_internal_transport_http_dto.SSOUnlinkResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "example": "microsoft" + }, + "unlinked": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_sso_unlink" + } + } + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINChangeAttributes": { + "type": "object", + "required": [ + "confirm_pin", + "current_pin", + "new_pin" + ], + "properties": { + "confirm_pin": { + "type": "string", + "example": "654321" + }, + "current_pin": { + "type": "string", + "example": "123456" + }, + "new_pin": { + "type": "string", + "example": "654321" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINChangeData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINChangeAttributes" + }, + "type": { + "type": "string", + "example": "auth_pin_change" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINChangeRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINChangeData" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINSetupAttributes": { + "type": "object", + "required": [ + "confirm_pin", + "pin" + ], + "properties": { + "confirm_pin": { + "type": "string", + "example": "123456" + }, + "pin": { + "type": "string", + "example": "123456" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINSetupData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINSetupAttributes" + }, + "type": { + "type": "string", + "example": "auth_pin_setup" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINSetupRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINSetupData" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINSetupResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "configured": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_pin_setup" + } + } + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINVerifyAttributes": { + "type": "object", + "required": [ + "action", + "pin" + ], + "properties": { + "action": { + "type": "string", + "example": "a546de2f-f1e6-4c2c-ab03-9b4bf39f4d74" + }, + "pin": { + "type": "string", + "example": "123456" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINVerifyData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINVerifyAttributes" + }, + "type": { + "type": "string", + "example": "auth_pin_verify" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINVerifyRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SecurityPINVerifyData" + } + } + }, + "wucher_internal_transport_http_dto.SecurityPINVerifyResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "action_token": { + "type": "string", + "example": "pinv_1234567890abcdef" + }, + "action_token_ttl_ms": { + "type": "integer", + "example": 900000 + }, + "verified": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_pin_verify" + } + } + } + } + }, + "wucher_internal_transport_http_dto.SetPasswordAttributes": { + "type": "object", + "required": [ + "password", + "token" + ], + "properties": { + "password": { + "type": "string", + "minLength": 8, + "example": "StrongP@ssw0rd" + }, + "token": { + "type": "string", + "example": "kP8k2..." + } + } + }, + "wucher_internal_transport_http_dto.SetPasswordData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SetPasswordAttributes" + }, + "type": { + "type": "string", + "example": "auth_set_password" + } + } + }, + "wucher_internal_transport_http_dto.SetPasswordRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.SetPasswordData" + } + } + }, + "wucher_internal_transport_http_dto.StaffProfileAttributes": { + "type": "object", + "properties": { + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "role_label": { + "type": "string", + "maxLength": 120 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.StaffProfileUpdateAttributes": { + "type": "object", + "properties": { + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "role_label": { + "type": "string", + "maxLength": 120 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.TOTPConfirmAttributes": { + "type": "object", + "required": [ + "code", + "user_id" + ], + "properties": { + "code": { + "type": "string", + "example": "123456" + }, + "user_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.TOTPConfirmData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPConfirmAttributes" + }, + "type": { + "type": "string", + "example": "auth_totp_confirm" + } + } + }, + "wucher_internal_transport_http_dto.TOTPConfirmRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPConfirmData" + } + } + }, + "wucher_internal_transport_http_dto.TOTPDisableAttributes": { + "type": "object", + "required": [ + "user_id" + ], + "properties": { + "user_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.TOTPDisableData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPDisableAttributes" + }, + "type": { + "type": "string", + "example": "auth_totp_disable" + } + } + }, + "wucher_internal_transport_http_dto.TOTPDisableRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPDisableData" + } + } + }, + "wucher_internal_transport_http_dto.TOTPResetSetupData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ReauthAttributes" + }, + "type": { + "type": "string", + "example": "auth_totp_reset_setup" + } + } + }, + "wucher_internal_transport_http_dto.TOTPResetSetupRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPResetSetupData" + } + } + }, + "wucher_internal_transport_http_dto.TOTPSetupAttributes": { + "type": "object", + "required": [ + "email", + "user_id" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "user_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.TOTPSetupData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPSetupAttributes" + }, + "type": { + "type": "string", + "example": "auth_totp_setup" + } + } + }, + "wucher_internal_transport_http_dto.TOTPSetupRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPSetupData" + } + } + }, + "wucher_internal_transport_http_dto.TOTPSetupResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "issuer": { + "type": "string", + "example": "Wucher" + }, + "otpauth_url": { + "type": "string", + "example": "otpauth://totp/Wucher:user@example.com?..." + }, + "secret": { + "type": "string", + "example": "JBSWY3DPEHPK3PXP" + } + } + }, + "type": { + "type": "string", + "example": "auth_totp_setup" + } + } + } + } + }, + "wucher_internal_transport_http_dto.TOTPVerifyAttributes": { + "type": "object", + "required": [ + "challenge_token", + "code" + ], + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "code": { + "type": "string", + "example": "123456" + } + } + }, + "wucher_internal_transport_http_dto.TOTPVerifyData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPVerifyAttributes" + }, + "type": { + "type": "string", + "example": "auth_totp_verify" + } + } + }, + "wucher_internal_transport_http_dto.TOTPVerifyRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TOTPVerifyData" + } + } + }, + "wucher_internal_transport_http_dto.TechnicianProfileAttributes": { + "type": "object", + "properties": { + "is_chief_technician": { + "type": "boolean" + }, + "is_responsible_complaints": { + "type": "boolean" + }, + "license_no": { + "type": "string", + "maxLength": 120 + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.TechnicianProfileUpdateAttributes": { + "type": "object", + "properties": { + "is_chief_technician": { + "type": "boolean" + }, + "is_responsible_complaints": { + "type": "boolean" + }, + "license_no": { + "type": "string", + "maxLength": 120 + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "note": { + "type": "string" + }, + "postcode": { + "type": "string", + "maxLength": 32 + }, + "short_name": { + "type": "string", + "maxLength": 120 + }, + "street_line": { + "type": "string", + "maxLength": 255 + } + } + }, + "wucher_internal_transport_http_dto.TimezoneUpdateAttributes": { + "type": "object", + "required": [ + "timezone" + ], + "properties": { + "timezone": { + "type": "string", + "example": "Asia/Jakarta" + } + } + }, + "wucher_internal_transport_http_dto.TimezoneUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TimezoneUpdateAttributes" + }, + "type": { + "type": "string", + "example": "auth_timezone_update" + } + } + }, + "wucher_internal_transport_http_dto.TimezoneUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.TimezoneUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.TokenResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "access_expires_in": { + "type": "integer", + "example": 900 + }, + "refresh_expires_in": { + "type": "integer", + "example": 2592000 + } + } + }, + "type": { + "type": "string", + "example": "auth_tokens" + } + } + } + } + }, + "wucher_internal_transport_http_dto.UserAttributes": { + "type": "object", + "properties": { + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "email": { + "type": "string", + "example": "user@example.com" + }, + "email_verified_at": { + "type": "string", + "example": "2026-02-05T10:00:00Z" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "is_previously_configured": { + "type": "boolean", + "example": false + }, + "last_name": { + "type": "string", + "example": "Doe" + }, + "linked_sso": { + "type": "boolean", + "example": false + }, + "mobile_phone": { + "type": "string", + "example": "+628123456789" + }, + "pin_configured": { + "type": "boolean", + "example": false + }, + "profile_picture": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserImage" + }, + "role": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleMini" + }, + "role_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "role_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d\"", + "\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"]" + ] + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.RoleMini" + } + }, + "timezone": { + "type": "string", + "example": "Asia/Jakarta" + }, + "totp_enabled": { + "type": "boolean", + "example": false + }, + "username": { + "type": "string", + "example": "john.doe" + }, + "webauthn_configured": { + "type": "boolean", + "example": false + } + } + }, + "wucher_internal_transport_http_dto.UserCreateAttributes": { + "type": "object", + "required": [ + "email", + "first_name", + "last_name", + "role_id", + "username" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "image_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90" + }, + "last_name": { + "type": "string", + "example": "Doe" + }, + "mobile_phone": { + "type": "string", + "example": "+628123456789" + }, + "role_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "role_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d\"", + "\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"]" + ] + }, + "timezone": { + "type": "string", + "example": "Asia/Jakarta" + }, + "username": { + "type": "string", + "maxLength": 100, + "minLength": 3, + "example": "john.doe" + } + } + }, + "wucher_internal_transport_http_dto.UserCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "user_create", + "user" + ], + "example": "user_create" + } + } + }, + "wucher_internal_transport_http_dto.UserCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserCreateData" + } + } + }, + "wucher_internal_transport_http_dto.UserDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.UserFoto": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://storage.example.com/base/lude.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026X-Amz-Expires=900\u0026X-Amz-Signature=abc123" + }, + "original_size_bytes": { + "type": "integer", + "example": 1024000 + }, + "thumbnail_download_url": { + "type": "string", + "example": "https://storage.example.com/base/lude.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026X-Amz-Expires=900\u0026X-Amz-Signature=abc123" + }, + "uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f" + } + } + }, + "wucher_internal_transport_http_dto.UserImage": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://example.com/presigned-url" + }, + "original_size_bytes": { + "type": "integer", + "example": 1024000 + }, + "thumbnail_download_url": { + "type": "string", + "example": "https://example.com/presigned-thumb-url" + }, + "uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f" + } + } + }, + "wucher_internal_transport_http_dto.UserListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.UserResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "user" + } + } + }, + "wucher_internal_transport_http_dto.UserResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "user" + } + } + } + } + }, + "wucher_internal_transport_http_dto.UserUpdateAttributes": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "image_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90" + }, + "last_name": { + "type": "string", + "example": "Doe" + }, + "mobile_phone": { + "type": "string", + "example": "+628123456789" + }, + "timezone": { + "type": "string", + "example": "Asia/Jakarta" + } + } + }, + "wucher_internal_transport_http_dto.UserUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "user_update", + "user" + ], + "example": "user_update" + } + } + }, + "wucher_internal_transport_http_dto.UserUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.UserUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.VerifyEmailResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "verified": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_verify_email" + } + } + } + } + }, + "wucher_internal_transport_http_dto.VocationAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "Pilot" + }, + "note": { + "type": "string", + "example": "Helicopter pilot vocation" + }, + "sortkey": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto.VocationCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "Pilot" + }, + "note": { + "type": "string", + "example": "Helicopter pilot vocation" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.VocationCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "vocation_create", + "vocation" + ], + "example": "vocation_create" + } + } + }, + "wucher_internal_transport_http_dto.VocationCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationCreateData" + } + } + }, + "wucher_internal_transport_http_dto.VocationDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.VocationListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto.VocationResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "vocation" + } + } + }, + "wucher_internal_transport_http_dto.VocationResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationResource" + } + } + }, + "wucher_internal_transport_http_dto.VocationUpdateAttributes": { + "type": "object", + "properties": { + "is_active": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "Pilot" + }, + "note": { + "type": "string", + "example": "Helicopter pilot vocation" + }, + "sortkey": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto.VocationUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "vocation_update", + "vocation" + ], + "example": "vocation_update" + } + } + }, + "wucher_internal_transport_http_dto.VocationUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.VocationUpdateData" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnBeginResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "challenge_ttl_ms": { + "type": "integer", + "example": 300000 + }, + "public_key": { + "type": "object", + "additionalProperties": true + } + } + }, + "type": { + "type": "string", + "example": "auth_webauthn_register_options" + } + } + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnLoginBeginAttributes": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string", + "example": "user@example.com" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnLoginBeginData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnLoginBeginAttributes" + }, + "type": { + "type": "string", + "example": "auth_webauthn_login_begin" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnLoginBeginRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnLoginBeginData" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnLoginFinishAttributes": { + "type": "object", + "required": [ + "challenge_token", + "credential" + ], + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "credential": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnLoginFinishData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnLoginFinishAttributes" + }, + "type": { + "type": "string", + "example": "auth_webauthn_login_finish" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnLoginFinishRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnLoginFinishData" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnRegisterBeginData": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "example": "auth_webauthn_register_begin" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnRegisterBeginRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnRegisterBeginData" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnRegisterFinishAttributes": { + "type": "object", + "required": [ + "challenge_token", + "credential" + ], + "properties": { + "challenge_token": { + "type": "string", + "example": "kP8k2..." + }, + "credential": { + "type": "array", + "items": { + "type": "integer" + } + }, + "name": { + "type": "string", + "maxLength": 100, + "example": "MacBook Touch ID" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnRegisterFinishData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnRegisterFinishAttributes" + }, + "type": { + "type": "string", + "example": "auth_webauthn_register_finish" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnRegisterFinishRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnRegisterFinishData" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnResetSetupData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.ReauthAttributes" + }, + "type": { + "type": "string", + "example": "auth_webauthn_reset_setup" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnResetSetupRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto.WebAuthnResetSetupData" + } + } + }, + "wucher_internal_transport_http_dto.WebAuthnResetSetupResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "reset": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "auth_webauthn_reset_setup" + } + } + } + } + }, + "wucher_internal_transport_http_dto_request.ActionSignoffSignAttributes": { + "type": "object", + "properties": { + "complaint_id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "flight_id": { + "description": "FlightID — REQUIRED for the fleet (complaint-independent) sign-off; scopes it to a\nflight so it no longer leaks across takeovers of the same aircraft. Ignored when\ncomplaint_id is set (that sign-off is keyed by the complaint).", + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "sign": { + "description": "Sign toggles the sign-off: true (or omitted) signs it, false unsigns (clears the\nsignature). Combine with complaint_id to toggle a specific complaint's sign-off.", + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_request.ActionSignoffSignData": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ActionSignoffSignAttributes" + }, + "type": { + "type": "string", + "example": "action_signoff" + } + } + }, + "wucher_internal_transport_http_dto_request.ActionSignoffSignRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ActionSignoffSignData" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateAttributes": { + "type": "object", + "required": [ + "base_id", + "checklists" + ], + "properties": { + "base_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "checklists": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateEntry" + } + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "air_rescuer_checklist_create" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateEntry": { + "type": "object", + "required": [ + "scope_code", + "title" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.AirRescuerChecklistItemInput" + } + }, + "position": { + "type": "integer", + "example": 1 + }, + "scope_code": { + "type": "string", + "example": "MO" + }, + "title": { + "type": "string", + "example": "Monday Checklist" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemCreateAttributes": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.AirRescuerChecklistItemInput" + } + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "air_rescuer_checklist_item_create" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderAttributes": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderEntry" + } + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderAttributes" + }, + "type": { + "type": "string", + "enum": [ + "air_rescuer_checklist_item_reorder" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderEntry": { + "type": "object", + "required": [ + "item_uuid", + "position" + ], + "properties": { + "item_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "position": { + "type": "integer", + "example": 2 + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemReorderData" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemUpdateAttributes": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Check radio" + }, + "position": { + "type": "integer", + "example": 2 + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "air_rescuer_checklist_item_update" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistItemUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistItemUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistUpdateAttributes": { + "type": "object", + "properties": { + "new_items": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.AirRescuerChecklistItemInput" + } + }, + "position": { + "type": "integer", + "example": 1 + }, + "scope_code": { + "type": "string", + "example": "MO" + }, + "title": { + "type": "string", + "example": "Monday Checklist Updated" + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "air_rescuer_checklist_update" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.AirRescuerChecklistUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.AirRescuerChecklistUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintActionTakenAttributes": { + "type": "object", + "properties": { + "action_taken": { + "description": "ActionTaken — corrective action text. Required when recording (create); on update an\nempty value clears it (allowed only while the action has not been signed off).", + "type": "string", + "example": "Tightened engine mount and rechecked torque" + }, + "aircraft_hours_at_fix": { + "type": "string", + "example": "1250.1" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintActionTakenData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintActionTakenAttributes" + }, + "type": { + "type": "string", + "enum": [ + "complaint_action_taken", + "complaint" + ], + "example": "complaint_action_taken" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintActionTakenRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintActionTakenData" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintCreateAttributes": { + "type": "object", + "required": [ + "description", + "helicopter_id" + ], + "properties": { + "aircraft_hours_at_report": { + "description": "AircraftHoursAtReport — OPTIONAL airframe hours recorded when the defect was reported.", + "type": "string", + "example": "1245.3" + }, + "description": { + "description": "Description — the defect description. REQUIRED.", + "type": "string", + "example": "Engine 2 N2 vibration intermittent during cruise above 110 KIAS" + }, + "flight_id": { + "description": "FlightID — the flight/report this complaint was filed in (attribution). OPTIONAL;\nopen-defect tracking stays per-helicopter so defects carry forward across flights.", + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "helicopter_id": { + "description": "HelicopterID — the helicopter this complaint is filed against. REQUIRED.", + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "mel_severity": { + "description": "MELSeverity — MEL (Minimum Equipment List) classification. OPTIONAL at creation:\nomit it to file the complaint as \"pending_mel\" and classify later. It MUST be\nclassified (via update) before recording action taken. A pending complaint does\nNOT ground the aircraft — grounding starts at classification. Values: 0 = NON-MEL\n(grounds immediately), 1 = A (grace 1 day), 2 = B (3 days), 3 = C (10 days), 4 = D (120 days).", + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "example": 2 + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "complaint_create", + "complaint" + ], + "example": "complaint_create" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintNSRSignAttributes": { + "type": "object", + "properties": { + "nsr_reason": { + "description": "NSRReason — OPTIONAL free-text justification for the NSR (MEL deferral) decision.", + "type": "string", + "example": "Released under MEL A, defect deferred" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintNSRSignData": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintNSRSignAttributes" + }, + "type": { + "type": "string", + "enum": [ + "complaint_nsr_sign", + "complaint_nsr" + ], + "example": "complaint_nsr_sign" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintNSRSignRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintNSRSignData" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintUpdateAttributes": { + "type": "object", + "properties": { + "aircraft_hours_at_report": { + "description": "AircraftHoursAtReport — airframe hours at report time.", + "type": "string", + "example": "1245.3" + }, + "description": { + "description": "Description — defect description.", + "type": "string", + "example": "Engine 2 vibration confirmed" + }, + "mel_severity": { + "description": "MELSeverity — MEL classification. 0 = NON-MEL (immediate AOG), 1 = A (1 day),\n2 = B (3 days), 3 = C (10 days), 4 = D (120 days). AOG after grace passes.", + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "example": 2 + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintUpdateData": { + "type": "object", + "required": [ + "attributes", + "id", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintUpdateAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "enum": [ + "complaint_update", + "complaint" + ], + "example": "complaint_update" + } + } + }, + "wucher_internal_transport_http_dto_request.ComplaintUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ComplaintUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterAnyDeleteData": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterAnyDeleteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterAnyDeleteData" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterCreateAttributes": { + "type": "object", + "required": [ + "base_id", + "duty_date" + ], + "properties": { + "base_id": { + "type": "string" + }, + "duty_date": { + "type": "string" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDetailsInput" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterShiftTime" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "duty_roster_create", + "duty_roster" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterCrewInput": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "shift_date": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterShiftDate" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewAttributes": { + "type": "object", + "properties": { + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDetailsInput" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewByIDAttributes": { + "type": "object", + "required": [ + "crew_id", + "roster_id" + ], + "properties": { + "crew_id": { + "type": "string" + }, + "roster_id": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewByIDData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewByIDAttributes" + }, + "type": { + "type": "string", + "enum": [ + "duty_roster_crew_delete", + "duty_roster_update", + "duty_roster" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewByIDRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewByIDData" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewAttributes" + }, + "type": { + "type": "string", + "enum": [ + "duty_roster_crew_delete", + "duty_roster_update", + "duty_roster" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDeleteCrewData" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterDetailsInput": { + "type": "object", + "properties": { + "doctor": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterCrewInput" + } + }, + "helper": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterCrewInput" + } + }, + "other_person": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterOtherPersonInput" + } + }, + "pilot": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterPilotCrewInput" + } + }, + "rescuer": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterCrewInput" + } + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterOtherPersonInput": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "guest@example.com" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Guest Person" + }, + "other_person_id": { + "description": "OtherPersonID — OPTIONAL master person id to reuse (from GET /contact/other).\nWhen set, name/mobile_phone/email are snapshotted from the master (may be omitted).", + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterPilotCrewInput": { + "type": "object", + "properties": { + "co_pilot": { + "type": "boolean" + }, + "crew_type": { + "type": "string", + "enum": [ + "main", + "additional" + ], + "example": "main" + }, + "examiner": { + "type": "boolean" + }, + "flight_instructor": { + "type": "boolean" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "line_checker": { + "type": "boolean" + }, + "shift_date": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterShiftDate" + }, + "supervisor": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterShiftDate": { + "type": "object", + "properties": { + "date_end": { + "type": "string", + "example": "2026-03-16" + }, + "date_start": { + "type": "string", + "example": "2026-03-16" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterShiftTime": { + "type": "object", + "properties": { + "shift_end": { + "type": "string", + "example": "21:00" + }, + "shift_start": { + "type": "string", + "example": "06:00" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterUpdateAttributes": { + "type": "object", + "properties": { + "base_id": { + "type": "string" + }, + "duty_date": { + "type": "string" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterDetailsInput" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterShiftTime" + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterUpdateAttributes" + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "duty_roster_update", + "duty_roster" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.DutyRosterUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.DutyRosterUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.EASAReleaseCreateAttributes": { + "type": "object", + "required": [ + "helicopter_id" + ], + "properties": { + "complaint_id": { + "description": "ComplaintID — OPTIONAL complaint (UUIDv7) this release closes. Set it to scope the\nrelease to a specific defect; leave empty for a standalone (aircraft-clear) release.", + "type": "string", + "example": "019d6774-1111-7fba-9ef4-3795c0da8a13" + }, + "easa_ac_hours": { + "type": "string", + "example": "1245.3" + }, + "easa_date": { + "type": "string", + "example": "2026-06-16" + }, + "easa_location": { + "type": "string", + "example": "LOWI" + }, + "easa_maint_manual_rev_airframe": { + "type": "string", + "example": "Rev 12" + }, + "easa_maint_manual_rev_engine": { + "type": "string", + "example": "Rev 8" + }, + "easa_permit_no": { + "type": "string", + "example": "145.A.50" + }, + "easa_signer_id": { + "description": "EASASignerID — OPTIONAL contact id (UUIDv7) of the signer/technician. Required only to sign.", + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "easa_wo_no": { + "type": "string", + "example": "WO-2026-001" + }, + "flight_id": { + "description": "FlightID — OPTIONAL flight context. Used for the standalone (no-complaint) sign-off gate.", + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "sign": { + "description": "Sign, when true, signs the release immediately at creation. Requires all\nrequired fields to be present; otherwise the request is rejected.", + "type": "boolean", + "example": false + } + } + }, + "wucher_internal_transport_http_dto_request.EASAReleaseCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.EASAReleaseCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "easa_release_create", + "easa_release" + ], + "example": "easa_release_create" + } + } + }, + "wucher_internal_transport_http_dto_request.EASAReleaseCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.EASAReleaseCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.EASAReleaseUpdateAttributes": { + "type": "object", + "properties": { + "easa_ac_hours": { + "type": "string", + "example": "1245.3" + }, + "easa_date": { + "type": "string", + "example": "2026-06-16" + }, + "easa_location": { + "type": "string", + "example": "LOWI" + }, + "easa_maint_manual_rev_airframe": { + "type": "string", + "example": "Rev 12" + }, + "easa_maint_manual_rev_engine": { + "type": "string", + "example": "Rev 8" + }, + "easa_permit_no": { + "type": "string", + "example": "145.A.50" + }, + "easa_signer_id": { + "description": "EASASignerID — OPTIONAL contact id (UUIDv7) of the signer/technician. Required only to sign.", + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "easa_wo_no": { + "type": "string", + "example": "WO-2026-001" + } + } + }, + "wucher_internal_transport_http_dto_request.EASAReleaseUpdateData": { + "type": "object", + "required": [ + "attributes", + "id", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.EASAReleaseUpdateAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "enum": [ + "easa_release_update", + "easa_release" + ], + "example": "easa_release_update" + } + } + }, + "wucher_internal_transport_http_dto_request.EASAReleaseUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.EASAReleaseUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FMReportUpdateAttributes": { + "type": "object", + "properties": { + "engine1_gpc_n1": { + "description": "Engine1GpcN1 - GPC/N1 value for engine 1. Optional.", + "type": "string", + "example": "GPC/N1 63.2" + }, + "engine1_ptc_n2": { + "description": "Engine1PtcN2 - PTC/N2 value for engine 1. Optional.", + "type": "string", + "example": "PTC/N2 61.8" + }, + "engine2_gpc_n1": { + "description": "Engine2GpcN1 - GPC/N1 value for engine 2. Optional.", + "type": "string", + "example": "GPC/N1 63.2" + }, + "engine2_ptc_n2": { + "description": "Engine2PtcN2 - PTC/N2 value for engine 2. Optional.", + "type": "string", + "example": "PTC/N2 61.8" + } + } + }, + "wucher_internal_transport_http_dto_request.FMReportUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FMReportUpdateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "fm_report_update", + "fm_report" + ], + "example": "fm_report_update" + } + } + }, + "wucher_internal_transport_http_dto_request.FMReportUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FMReportUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerAttachmentCreateAttributes": { + "type": "object", + "required": [ + "file_id", + "ref_id", + "ref_type" + ], + "properties": { + "category": { + "type": "string", + "example": "document" + }, + "file_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_primary": { + "type": "boolean", + "example": false + }, + "ref_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "ref_type": { + "type": "string", + "example": "helicopter" + }, + "sort_order": { + "type": "integer", + "example": 0 + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerAttachmentCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerAttachmentCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_attachment_create", + "file_manager_attachment" + ], + "example": "file_manager_attachment_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerAttachmentCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerAttachmentCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileAircraftBulkCreateItem": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileAircraftCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create", + "file_manager_file" + ], + "example": "file_manager_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileAircraftBulkCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileAircraftBulkCreateItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileAircraftCreateAttributes": { + "type": "object", + "required": [ + "aircraft_uuid", + "name", + "upload_intent_uuid" + ], + "properties": { + "aircraft_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "aircraft.jpg" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileBaseBulkCreateItem": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileBaseCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create", + "file_manager_file" + ], + "example": "file_manager_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileBaseBulkCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileBaseBulkCreateItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileBaseCreateAttributes": { + "type": "object", + "required": [ + "base_uuid", + "name", + "upload_intent_uuid" + ], + "properties": { + "base_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "base.jpg" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileCancelUploadAttributes": { + "type": "object", + "required": [ + "upload_intent_uuid" + ], + "properties": { + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileCancelUploadBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileCancelUploadData" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileCancelUploadData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileCancelUploadAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_cancel_upload" + ], + "example": "file_manager_file_cancel_upload" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileDULBulkCreateItem": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileDULCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create", + "file_manager_file" + ], + "example": "file_manager_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileDULBulkCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileDULBulkCreateItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileDULCreateAttributes": { + "type": "object", + "required": [ + "name", + "upload_intent_uuid" + ], + "properties": { + "dul_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "dul.jpg" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileMissionBulkCreateItem": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileMissionCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create", + "file_manager_file" + ], + "example": "file_manager_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileMissionBulkCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileMissionBulkCreateItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileMissionCreateAttributes": { + "type": "object", + "required": [ + "mission_uuid", + "name", + "upload_intent_uuid" + ], + "properties": { + "mission_uuid": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "name": { + "type": "string", + "example": "mission-brief.pdf" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileTakeoverBulkCreateItem": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileTakeoverCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create", + "file_manager_file" + ], + "example": "file_manager_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileTakeoverBulkCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileTakeoverBulkCreateItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileTakeoverCreateAttributes": { + "type": "object", + "required": [ + "name", + "upload_intent_uuid" + ], + "properties": { + "name": { + "type": "string", + "example": "takeover-report.pdf" + }, + "takeover_uuid": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileTemplateCreateAttributes": { + "type": "object", + "required": [ + "helicopter_id", + "name", + "template_uuid" + ], + "properties": { + "helicopter_id": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + }, + "name": { + "type": "string", + "example": "collabora-draft.docx" + }, + "takeover_id": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + }, + "template_uuid": { + "type": "string", + "example": "019f0386-0a00-7a4a-a2d8-30afc0e0c3ad" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileTemplateCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileTemplateCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create_from_template", + "file_manager_file_create" + ] + } + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUpdateAttributes": { + "type": "object", + "properties": { + "folder_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "policy-v2.pdf" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_update", + "file_manager_file" + ], + "example": "file_manager_file_update" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentAttributes": { + "type": "object", + "required": [ + "content_type", + "name", + "size_bytes" + ], + "properties": { + "content_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "policy-final.pdf" + }, + "size_bytes": { + "type": "integer", + "minimum": 0, + "example": 1024 + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentData" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUploadIntentAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_upload_intent" + ], + "example": "file_manager_file_upload_intent" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUserBulkCreateItem": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUserCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_file_create", + "file_manager_file" + ], + "example": "file_manager_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUserBulkCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFileUserBulkCreateItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFileUserCreateAttributes": { + "type": "object", + "required": [ + "name", + "upload_intent_uuid", + "user_uuid" + ], + "properties": { + "name": { + "type": "string", + "example": "profile.jpg" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "user_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFolderCreateAttributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "example": "operations" + }, + "parent_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFolderCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFolderCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_folder_create", + "file_manager_folder" + ], + "example": "file_manager_folder_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFolderCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFolderCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFolderUpdateAttributes": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "operations-updated" + }, + "parent_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFolderUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFolderUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "file_manager_folder_update", + "file_manager_folder" + ], + "example": "file_manager_folder_update" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerFolderUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerFolderUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerNodeMoveBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerNodeMoveItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerNodeMoveItem": { + "type": "object", + "required": [ + "uuid" + ], + "properties": { + "destination_folder_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerNodePurgeBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FileManagerNodePurgeItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FileManagerNodePurgeItem": { + "type": "object", + "required": [ + "uuid" + ], + "properties": { + "uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusCreateAttributes": { + "type": "object", + "required": [ + "helicopter_id" + ], + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusFileAttributes" + } + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "maintenance_schedules": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.MaintenanceScheduleAttributes" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "fleet_status_create", + "fleet_status" + ], + "example": "fleet_status_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusSummaryAirframeUpdate": { + "type": "object", + "properties": { + "cycles": { + "type": "number", + "example": 320 + }, + "hours": { + "type": "number", + "example": 6610.5 + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusSummaryEngineUpdate": { + "type": "object", + "properties": { + "ccc": { + "type": "number", + "example": 44 + }, + "gpc_ng_n1": { + "type": "number", + "example": 98.7 + }, + "hours": { + "type": "number", + "example": 8001.7 + }, + "ptc_nf_n2": { + "type": "number", + "example": 97.2 + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusSummaryUpdate": { + "type": "object", + "properties": { + "airframe": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusSummaryAirframeUpdate" + }, + "engine_1": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusSummaryEngineUpdate" + }, + "engine_2": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusSummaryEngineUpdate" + }, + "flight_reports": { + "type": "number", + "example": 80 + }, + "hook_releases": { + "type": "number", + "example": 10 + }, + "landings": { + "type": "number", + "example": 120 + }, + "rotor_brake_cycles": { + "type": "number", + "example": 8 + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusUpdateAttributes": { + "type": "object", + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusFileAttributes" + } + }, + "helicopter_id": { + "type": "string" + }, + "maintenance_schedules": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.MaintenanceScheduleAttributes" + } + }, + "summary": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusSummaryUpdate" + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusUpdateAttributes" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "enum": [ + "fleet_status_update", + "fleet_status" + ], + "example": "fleet_status_update" + } + } + }, + "wucher_internal_transport_http_dto_request.FleetStatusUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FleetStatusUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateAttributes": { + "type": "object", + "required": [ + "mission_id" + ], + "properties": { + "co_pilot_id": { + "type": "string" + }, + "customer_name": { + "type": "string" + }, + "delivery_note_number": { + "type": "string" + }, + "engine": { + "type": "string" + }, + "flight_duration_seconds": { + "type": "integer" + }, + "flight_landing": { + "type": "string" + }, + "flight_plan_distance": { + "type": "number" + }, + "flight_plan_time_seconds": { + "type": "integer" + }, + "flight_plan_true_course": { + "type": "number" + }, + "flight_positioning": { + "type": "boolean", + "example": false + }, + "flight_red_seconds": { + "type": "integer" + }, + "flight_take_off": { + "type": "string" + }, + "from_hospital_id": { + "type": "string" + }, + "from_icao_id": { + "type": "string" + }, + "fuel_after_flight": { + "type": "number" + }, + "fuel_before_flight": { + "type": "number" + }, + "fuel_planning": { + "type": "number" + }, + "fuel_upload": { + "type": "number" + }, + "hec": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECAttributes" + }, + "heslo": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHESLOAttributes" + }, + "hook_releases": { + "type": "integer" + }, + "landing_count": { + "type": "integer" + }, + "logging": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOLoggingAttributes" + }, + "max_n1": { + "type": "number" + }, + "max_n2": { + "type": "number" + }, + "mission_id": { + "type": "string" + }, + "other_information": { + "type": "string" + }, + "pax_count": { + "type": "integer" + }, + "rotor_brake_cycle": { + "type": "integer" + }, + "ticket_no": { + "type": "string" + }, + "to_hospital_id": { + "type": "string" + }, + "to_icao_id": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "flight_data_create", + "flight_data" + ], + "example": "flight_data_create" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECAttributes": { + "type": "object", + "properties": { + "flights": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECFlight" + } + }, + "loads": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECLoad" + } + }, + "slings": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECSling" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECFlight": { + "type": "object", + "properties": { + "equipment_id": { + "description": "Facility category: hec-rope-label.", + "type": "string", + "example": "string" + }, + "equipment_name": { + "type": "string", + "example": "string" + }, + "hec_cycle": { + "type": "integer", + "example": 0 + }, + "rot": { + "type": "integer", + "example": 0 + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECLoad": { + "type": "object", + "properties": { + "load_category": { + "description": "Weight band for the HEC load, e.g. \"bis 199 kg\", \"200-600 kg\", \"600-800 kg\", \"800-1000 kg\".", + "type": "string", + "example": "200-600 kg" + }, + "quantity": { + "type": "integer", + "example": 3 + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHECSling": { + "type": "object", + "properties": { + "sling_id": { + "description": "Facility category: hec-rope-length.", + "type": "string", + "example": "string" + }, + "sling_name": { + "type": "string", + "example": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHESLOAttributes": { + "type": "object", + "properties": { + "flights": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHESLOFlight" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHESLOFlight": { + "type": "object", + "properties": { + "heslo_rope_label_id": { + "description": "Facility category: heslo-rope-label.", + "type": "string", + "example": "string" + }, + "heslo_rope_label_name": { + "type": "string", + "example": "string" + }, + "rot": { + "type": "integer", + "example": 0 + }, + "slings": { + "description": "Facility category: heslo-rope-length.", + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOHESLOSling" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOHESLOSling": { + "type": "object", + "properties": { + "sling_id": { + "description": "Facility category: heslo-rope-length.", + "type": "string", + "example": "string" + }, + "sling_name": { + "type": "string", + "example": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOLoggingAttributes": { + "type": "object", + "properties": { + "slings": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateSPOLoggingSling" + } + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataCreateSPOLoggingSling": { + "type": "object", + "properties": { + "sling_id": { + "description": "Facility category: heslo-hook.", + "type": "string", + "example": "string" + }, + "sling_name": { + "type": "string", + "example": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "flight_data_update" + ], + "example": "flight_data_update" + } + } + }, + "wucher_internal_transport_http_dto_request.FlightDataUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.FlightDataUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterCreateAttributes": { + "type": "object", + "required": [ + "designation", + "identifier", + "type" + ], + "properties": { + "aog": { + "type": "boolean", + "example": false + }, + "aog_reason": { + "type": "string", + "example": "Main rotor gearbox fault" + }, + "consumption_lt_min": { + "type": "number", + "example": 4.1 + }, + "designation": { + "type": "string", + "example": "OE-XHZ" + }, + "dry": { + "type": "boolean", + "example": false + }, + "engine_1": { + "type": "string", + "example": "TM" + }, + "engine_2": { + "type": "string", + "example": "TM" + }, + "file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90" + }, + "identifier": { + "type": "string", + "example": "ABC" + }, + "igb": { + "type": "boolean", + "example": false + }, + "is_active": { + "type": "boolean", + "example": true + }, + "lh": { + "type": "boolean", + "example": true + }, + "mgb": { + "type": "boolean", + "example": true + }, + "note": { + "type": "string", + "example": "Primary HEMS helicopter" + }, + "nr1": { + "type": "boolean", + "example": true + }, + "nr2": { + "type": "boolean", + "example": true + }, + "rh": { + "type": "boolean", + "example": true + }, + "sortkey": { + "type": "integer", + "example": 10 + }, + "tgb": { + "type": "boolean", + "example": true + }, + "type": { + "type": "string", + "example": "EC-135" + }, + "utc_offset": { + "type": "integer", + "example": -1 + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_create", + "helicopter" + ], + "example": "helicopter_create" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileCancelUploadAttributes": { + "type": "object", + "required": [ + "upload_intent_uuid" + ], + "properties": { + "upload_intent_uuid": { + "type": "string", + "example": "019d6d0d-2de2-7db1-80a2-2862d43af620" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileCancelUploadBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileCancelUploadData" + } + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileCancelUploadData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileCancelUploadAttributes" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_file_cancel_upload" + ], + "example": "helicopter_file_cancel_upload" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileCreateAttributes": { + "type": "object", + "required": [ + "file_name", + "section", + "upload_intent_uuid" + ], + "properties": { + "file_name": { + "type": "string", + "example": "Before Flight Checklist.pdf" + }, + "folder_uuid": { + "type": "string", + "example": "019d6d0d-8c24-72e3-9f58-c9ea0fbf0bb1" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "is_mandatory": { + "type": "boolean", + "example": true + }, + "position": { + "type": "integer", + "example": 1 + }, + "section": { + "type": "string", + "enum": [ + "bfi", + "fpwb", + "afi" + ], + "example": "bfi" + }, + "upload_intent_uuid": { + "type": "string", + "example": "019d6d0d-2de2-7db1-80a2-2862d43af620" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileCreateBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileCreateData" + } + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_file_create", + "helicopter_file" + ], + "example": "helicopter_file_create" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileDeleteData": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_file_delete", + "helicopter_file" + ], + "example": "helicopter_file_delete" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileDeleteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileDeleteData" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileUpdateAttributes": { + "type": "object", + "properties": { + "file_name": { + "type": "string", + "example": "Before Flight Checklist v2.pdf" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "is_mandatory": { + "type": "boolean", + "example": true + }, + "position": { + "type": "integer", + "example": 1 + }, + "section": { + "type": "string", + "enum": [ + "bfi", + "fpwb", + "afi" + ], + "example": "fpwb" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileUpdateBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileUpdateData" + } + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileUpdateData": { + "type": "object", + "required": [ + "attributes", + "id", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileUpdateAttributes" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_file_update", + "helicopter_file" + ], + "example": "helicopter_file_update" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileUploadAttributes": { + "type": "object", + "required": [ + "content_type", + "name", + "size_bytes" + ], + "properties": { + "content_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "Before Flight Checklist.pdf" + }, + "size_bytes": { + "type": "integer", + "minimum": 0, + "example": 1024 + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileUploadBulkRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileUploadData" + } + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterFileUploadData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterFileUploadAttributes" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_file_upload" + ], + "example": "helicopter_file_upload" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterUpdateAttributes": { + "type": "object", + "properties": { + "aog": { + "type": "boolean", + "example": false + }, + "aog_reason": { + "type": "string", + "example": "Main rotor gearbox fault" + }, + "consumption_lt_min": { + "type": "number", + "example": 4.1 + }, + "designation": { + "type": "string", + "example": "OE-XHZ" + }, + "dry": { + "type": "boolean", + "example": false + }, + "engine_1": { + "type": "string", + "example": "TM" + }, + "engine_2": { + "type": "string", + "example": "TM" + }, + "file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90" + }, + "identifier": { + "type": "string", + "example": "ABC" + }, + "igb": { + "type": "boolean", + "example": false + }, + "is_active": { + "type": "boolean", + "example": true + }, + "lh": { + "type": "boolean", + "example": true + }, + "mgb": { + "type": "boolean", + "example": true + }, + "note": { + "type": "string", + "example": "Primary HEMS helicopter" + }, + "nr1": { + "type": "boolean", + "example": true + }, + "nr2": { + "type": "boolean", + "example": true + }, + "rh": { + "type": "boolean", + "example": true + }, + "sortkey": { + "type": "integer", + "example": 10 + }, + "tgb": { + "type": "boolean", + "example": true + }, + "type": { + "type": "string", + "example": "EC-135" + }, + "utc_offset": { + "type": "integer", + "example": -1 + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "helicopter_update", + "helicopter" + ], + "example": "helicopter_update" + } + } + }, + "wucher_internal_transport_http_dto_request.HelicopterUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.HelicopterUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionUpsertAttributes" + }, + "type": { + "type": "string", + "enum": [ + "last_flights_inspection_create", + "last_flights_inspection" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionDeleteAttributes": { + "type": "object", + "required": [ + "flight_inspection_id" + ], + "properties": { + "flight_inspection_id": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionDeleteData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionDeleteAttributes" + }, + "type": { + "type": "string", + "enum": [ + "last_flights_inspection_delete", + "last_flights_inspection" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionDeleteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionDeleteData" + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionUpsertAttributes" + }, + "type": { + "type": "string", + "enum": [ + "last_flights_inspection_update", + "last_flights_inspection" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.LastFlightsInspectionUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.LastFlightsInspectionUpsertAttributes": { + "type": "object", + "required": [ + "flight_inspection_id" + ], + "properties": { + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcFileChecklistItem" + } + }, + "flight_inspection_id": { + "type": "string" + }, + "hydraulic_lh_checked": { + "type": "string" + }, + "hydraulic_rh_checked": { + "type": "string" + }, + "note": { + "type": "string" + }, + "oil_engine_nr1_checked": { + "type": "string" + }, + "oil_engine_nr2_checked": { + "type": "string" + }, + "oil_transmission_igb_checked": { + "type": "string" + }, + "oil_transmission_mgb_checked": { + "type": "string" + }, + "oil_transmission_tgb_checked": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.MCFCompleteAttributes": { + "type": "object", + "properties": { + "af_hours": { + "type": "string", + "example": "1245.3" + }, + "date": { + "type": "string", + "example": "2026-06-17" + }, + "landing_count": { + "type": "integer", + "example": 3 + }, + "notes": { + "type": "string", + "example": "Vibration within limits after check flight" + }, + "result": { + "type": "string", + "enum": [ + "passed", + "failed" + ], + "example": "passed" + }, + "sign": { + "type": "boolean", + "example": true + }, + "utc_time": { + "type": "string", + "example": "2026-06-17T08:30:00Z" + } + } + }, + "wucher_internal_transport_http_dto_request.MCFCompleteData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MCFCompleteAttributes" + }, + "type": { + "type": "string", + "enum": [ + "mcf_complete", + "mcf_sign", + "mcf" + ], + "example": "mcf_sign" + } + } + }, + "wucher_internal_transport_http_dto_request.MCFCompleteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MCFCompleteData" + } + } + }, + "wucher_internal_transport_http_dto_request.MCFSetAttributes": { + "type": "object", + "properties": { + "active": { + "type": "boolean", + "example": true + }, + "af_hours": { + "type": "string", + "example": "1245.3" + }, + "date": { + "type": "string", + "example": "2026-06-17" + }, + "flight_id": { + "description": "FlightID — OPTIONAL flight this check flight is performed on (on activate).", + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "landing_count": { + "type": "integer", + "example": 3 + }, + "notes": { + "type": "string", + "example": "MCF required after engine mount work" + }, + "result": { + "type": "string", + "enum": [ + "passed", + "failed" + ], + "example": "passed" + }, + "utc_time": { + "type": "string", + "example": "2026-06-17T08:30:00Z" + } + } + }, + "wucher_internal_transport_http_dto_request.MCFSetData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MCFSetAttributes" + }, + "type": { + "type": "string", + "enum": [ + "mcf_set", + "mcf" + ], + "example": "mcf_set" + } + } + }, + "wucher_internal_transport_http_dto_request.MCFSetRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MCFSetData" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsCreateAttributes": { + "type": "object", + "required": [ + "company_name" + ], + "properties": { + "company_name": { + "type": "string", + "example": "Wucher" + }, + "cover_file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "logo_file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "subtitle": { + "type": "string", + "example": "Flight operations platform" + }, + "title": { + "type": "string", + "example": "Welcome to Wucher" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "master_settings_create", + "master_settings" + ], + "example": "master_settings_create" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsMicrosoftEntraUpsertAttributes": { + "type": "object", + "required": [ + "MS_ENTRA_AUTHORITY", + "MS_ENTRA_CLIENT_ID", + "MS_ENTRA_REDIRECT_URL", + "MS_ENTRA_SCOPES", + "MS_ENTRA_TENANT_ID" + ], + "properties": { + "MS_ENTRA_AUTHORITY": { + "type": "string", + "example": "https://login.microsoftonline.com/" + }, + "MS_ENTRA_CLIENT_ID": { + "type": "string", + "example": "your-client-id" + }, + "MS_ENTRA_CLIENT_SECRET": { + "type": "string", + "example": "********" + }, + "MS_ENTRA_REDIRECT_URL": { + "type": "string", + "example": "https://example.com/api/v1/auth/microsoft/callback" + }, + "MS_ENTRA_SCOPES": { + "type": "string", + "example": "openid,profile,email" + }, + "MS_ENTRA_TENANT_ID": { + "type": "string", + "example": "your-tenant-id" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsMicrosoftEntraUpsertData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsMicrosoftEntraUpsertAttributes" + }, + "type": { + "type": "string", + "enum": [ + "master_settings_microsoft_entra_upsert", + "master_settings" + ], + "example": "master_settings_microsoft_entra_upsert" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsMicrosoftEntraUpsertRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsMicrosoftEntraUpsertData" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsUpdateAttributes": { + "type": "object", + "properties": { + "company_name": { + "type": "string", + "example": "Wucher" + }, + "cover_file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "logo_file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "subtitle": { + "type": "string", + "example": "Flight operations platform" + }, + "title": { + "type": "string", + "example": "Welcome to Wucher" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsUpdateAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "enum": [ + "master_settings_update", + "master_settings" + ], + "example": "master_settings_update" + } + } + }, + "wucher_internal_transport_http_dto_request.MasterSettingsUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MasterSettingsUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionCreateAttributes": { + "type": "object", + "required": [ + "flight_id", + "type" + ], + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionFileInput" + } + }, + "flight_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "note": { + "type": "string", + "example": "Mission note" + }, + "subtype_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a99" + }, + "type": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "mission_create", + "mission" + ], + "example": "mission_create" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionFileInput": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "mission-brief.pdf" + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionTypeCreateAttributes": { + "type": "object", + "required": [ + "code_type", + "name" + ], + "properties": { + "code_type": { + "type": "string", + "example": "HEMS" + }, + "name": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionTypeCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionTypeCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "mission_type_create", + "mission_type" + ], + "example": "mission_type_create" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionTypeCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionTypeCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionUpdateAttributes": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionFileInput" + } + }, + "note": { + "type": "string", + "example": "Mission note" + }, + "subtype_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a99" + }, + "type": { + "type": "string", + "example": "SPO" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionUpdateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "mission_update", + "mission" + ], + "example": "mission_update" + } + } + }, + "wucher_internal_transport_http_dto_request.MissionUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.MissionUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.OtherPersonAttributesInput": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "guest@example.com" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Guest Person" + } + } + }, + "wucher_internal_transport_http_dto_request.OtherPersonRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.OtherPersonRequestData" + } + } + }, + "wucher_internal_transport_http_dto_request.OtherPersonRequestData": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.OtherPersonAttributesInput" + }, + "type": { + "type": "string", + "example": "other_person" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcBeforeInspectionAttributes": { + "type": "object", + "properties": { + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcFileChecklistItem" + } + }, + "fuel_added_amount": { + "type": "number" + }, + "fuel_amount": { + "type": "number" + }, + "fuel_unit": { + "type": "string" + }, + "hydraulic_lh_checked": { + "type": "boolean" + }, + "hydraulic_rh_checked": { + "type": "boolean" + }, + "note": { + "type": "string" + }, + "oil_engine_nr1_checked": { + "type": "boolean" + }, + "oil_engine_nr2_checked": { + "type": "boolean" + }, + "oil_transmission_igb_checked": { + "type": "boolean" + }, + "oil_transmission_mgb_checked": { + "type": "boolean" + }, + "oil_transmission_tgb_checked": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcCreateAttributes": { + "type": "object", + "required": [ + "flight_id", + "helicopter_id", + "inspection" + ], + "properties": { + "flight_id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcInspectionCreateAttributes" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "reserve_ac_create", + "reserve_ac" + ], + "example": "reserve_ac_create" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcDeleteData": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcDeleteRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcDeleteData" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcFileChecklistItem": { + "type": "object", + "required": [ + "helicopter_file_id" + ], + "properties": { + "helicopter_file_id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-111111111111" + }, + "is_done": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcFleetStatusFileAttributes": { + "type": "object", + "properties": { + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcFileChecklistItem" + } + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcInspectionCreateAttributes": { + "type": "object", + "required": [ + "before", + "inspection_date", + "prepare" + ], + "properties": { + "before": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcBeforeInspectionAttributes" + }, + "fleet_status_file": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcFleetStatusFileAttributes" + }, + "inspection_date": { + "type": "string", + "example": "2026-04-15" + }, + "prepare": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcPrepareInspectionAttributes" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcInspectionUpdateAttributes": { + "type": "object", + "properties": { + "before": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcBeforeInspectionAttributes" + }, + "inspection_date": { + "type": "string", + "example": "2026-04-15" + }, + "prepare": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcPrepareInspectionAttributes" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcPrepareInspectionAttributes": { + "type": "object", + "properties": { + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcFileChecklistItem" + } + }, + "notam_briefing": { + "type": "boolean" + }, + "operational_flight_plan": { + "type": "boolean" + }, + "weather_briefing": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcUpdateAttributes": { + "type": "object", + "properties": { + "flight_id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcInspectionUpdateAttributes" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcUpdateAttributes" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "enum": [ + "reserve_ac_update", + "reserve_ac" + ], + "example": "reserve_ac_update" + } + } + }, + "wucher_internal_transport_http_dto_request.ReserveAcUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverBaseRef": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverCreateAttributes": { + "type": "object", + "required": [ + "duty_date" + ], + "properties": { + "base_id": { + "type": "string" + }, + "duty_date": { + "type": "string" + }, + "edited_template_file_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateFileInput" + } + }, + "helicopter_id": { + "type": "string" + }, + "inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcInspectionCreateAttributes" + }, + "notes": { + "type": "string" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterDetailsInput" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverCreateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateAttributes" + }, + "type": { + "type": "string", + "enum": [ + "takeover_create", + "takeover" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverCreateFileInput": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + }, + "name": { + "type": "string", + "example": "takeover-report.pdf" + }, + "upload_intent_uuid": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverCreateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateData" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverRosterCrewInput": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverRosterDetailsInput": { + "type": "object", + "properties": { + "doctor": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterCrewInput" + } + }, + "helper": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterCrewInput" + } + }, + "other_person": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterOtherPersonInput" + } + }, + "pilot": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterPilotInput" + } + }, + "rescuer": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterCrewInput" + } + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverRosterOtherPersonInput": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "guest@example.com" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Guest Person" + }, + "other_person_id": { + "description": "OtherPersonID — OPTIONAL master person id to reuse (from GET /contact/other).", + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverRosterPilotInput": { + "type": "object", + "properties": { + "co_pilot": { + "type": "boolean" + }, + "crew_type": { + "type": "string", + "enum": [ + "main", + "additional" + ], + "example": "main" + }, + "examiner": { + "type": "boolean" + }, + "flight_instructor": { + "type": "boolean" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "line_checker": { + "type": "boolean" + }, + "supervisor": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverUpdateAttributes": { + "type": "object", + "properties": { + "base_id": { + "type": "string" + }, + "bases": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverBaseRef" + }, + "duty_date": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateFileInput" + } + }, + "files_add": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateFileInput" + } + }, + "files_remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "files_replace": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverCreateFileInput" + } + }, + "helicopter_id": { + "type": "string" + }, + "inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.ReserveAcInspectionCreateAttributes" + }, + "notes": { + "type": "string" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterDetailsInput" + }, + "roster_details": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverRosterDetailsInput" + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverUpdateData": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverUpdateAttributes" + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "takeover_update", + "takeover" + ] + } + } + }, + "wucher_internal_transport_http_dto_request.TakeoverUpdateRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_request.TakeoverUpdateData" + } + } + }, + "wucher_internal_transport_http_dto_response.ActionSignoffAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "flight_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "signed": { + "description": "Signed — true once checked; gates the maintenance release form.", + "type": "boolean", + "example": false + }, + "signed_at": { + "description": "SignedAt / SignedBy — when/who signed; empty until signed.", + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "signed_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "signed_by_name": { + "description": "SignedByName / SignedByShort — resolved display name + 3-letter short of the signer.", + "type": "string", + "example": "John Doe" + }, + "signed_by_short": { + "type": "string", + "example": "JDO" + }, + "updated_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.ActionSignoffResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ActionSignoffAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "example": "action_signoff" + } + } + }, + "wucher_internal_transport_http_dto_response.ActionSignoffResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ActionSignoffResource" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistAttributes": { + "type": "object", + "properties": { + "bases": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.AirRescuerChecklistBaseInfo" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemResource" + } + }, + "position": { + "type": "integer", + "example": 1 + }, + "scope_code": { + "type": "string", + "example": "TU" + }, + "scope_code_de": { + "type": "string", + "example": "DI" + }, + "title": { + "type": "string", + "example": "Monday Checklist" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistBaseGroupAttributes": { + "type": "object", + "properties": { + "bases": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.AirRescuerChecklistBaseInfo" + }, + "tabs": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistTabResource" + } + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistBaseGroupResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistBaseGroupAttributes" + }, + "id": { + "type": "string", + "example": "base-018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "air_rescuer_checklist_group" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistResource" + } + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistCATChecklistSummary": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "position": { + "type": "integer", + "example": 1 + }, + "title": { + "type": "string", + "example": "Cockpit Flugretter" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistCATGroup": { + "type": "object", + "properties": { + "checklists": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistCATChecklistSummary" + } + }, + "scope_code": { + "type": "string", + "example": "DA" + }, + "scope_code_de": { + "type": "string", + "example": "TA" + }, + "scope_code_name": { + "type": "string", + "example": "Daily" + }, + "scope_code_name_de": { + "type": "string", + "example": "Taeglich" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistGroupedDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistBaseGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 8 + }, + "records_total": { + "type": "integer", + "example": 8 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistGroupedListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistBaseGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "total_bases": { + "type": "integer", + "example": 8 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistItemAttributes": { + "type": "object", + "properties": { + "checklist_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Check radio" + }, + "position": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistItemBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemData" + } + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistItemData": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "air_rescuer_checklist_item" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistItemResource": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Oxygen supply check" + }, + "position": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistItemResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemData" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "air_rescuer_checklist" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistResource" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistTabChecklistDetail": { + "type": "object", + "properties": { + "checklist_items": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistItemResource" + } + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "position": { + "type": "integer", + "example": 1 + }, + "title": { + "type": "string", + "example": "Monday Checklist" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.AirRescuerChecklistTabResource": { + "type": "object", + "properties": { + "checklists": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistTabChecklistDetail" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.AirRescuerChecklistCATGroup" + } + }, + "scope_code": { + "type": "string", + "example": "DA" + }, + "scope_code_de": { + "type": "string", + "example": "TA" + }, + "scope_code_name": { + "type": "string", + "example": "Daily" + }, + "scope_code_name_de": { + "type": "string", + "example": "Taeglich" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintAttributes": { + "type": "object", + "properties": { + "action_signed": { + "type": "boolean", + "example": true + }, + "action_signed_at": { + "type": "string", + "example": "2026-06-15T03:04:05Z" + }, + "action_signed_by_name": { + "type": "string", + "example": "John Doe" + }, + "action_signed_by_short": { + "type": "string", + "example": "JDO" + }, + "action_taken": { + "type": "string", + "example": "tightened mount" + }, + "aircraft_hours_at_fix": { + "type": "string", + "example": "1250.1" + }, + "aircraft_hours_at_report": { + "type": "string", + "example": "1245.3" + }, + "created_at": { + "type": "string", + "example": "2026-06-15T03:04:05Z" + }, + "description": { + "type": "string", + "example": "Engine 2 N2 vibration intermittent during cruise above 110 KIAS" + }, + "flight_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusHelicopter" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "mel": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintMEL" + }, + "nsr": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintNSR" + }, + "reported_at": { + "type": "string", + "example": "2026-06-15T03:04:05Z" + }, + "reported_by": { + "type": "string", + "example": "John Doe" + }, + "reported_by_short": { + "type": "string", + "example": "JDO" + }, + "status": { + "type": "string", + "enum": [ + "active_non_mel", + "active_mel", + "serviced" + ], + "example": "active_mel" + }, + "updated_at": { + "type": "string", + "example": "2026-06-15T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintGroupedDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintHelicopterGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 1 + }, + "records_total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintGroupedListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintHelicopterGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintHelicopterGroupAttributes": { + "type": "object", + "properties": { + "complaints": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResource" + } + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusHelicopter" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintHelicopterGroupResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintHelicopterGroupAttributes" + }, + "type": { + "type": "string", + "example": "complaint_helicopter_group" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintMEL": { + "type": "object", + "properties": { + "classified_at": { + "description": "ClassifiedAt / ClassifiedByName / ClassifiedByShort — when/who classified the MEL.", + "type": "string", + "example": "2026-06-15T03:04:05Z" + }, + "classified_by_name": { + "type": "string", + "example": "John Doe" + }, + "classified_by_short": { + "type": "string", + "example": "JDO" + }, + "deadline": { + "description": "Deadline — grace deadline; after this the aircraft is AOG. null for NON-MEL.", + "type": "string", + "example": "2026-06-18T03:04:05Z" + }, + "severity": { + "description": "Severity — MEL class: 0=NON-MEL, 1=A, 2=B, 3=C, 4=D. Always set (classification is required at creation).", + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "example": 2 + }, + "severity_label": { + "description": "SeverityLabel — human label of the severity (NON-MEL/A/B/C/D).", + "type": "string", + "example": "B" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintNSR": { + "type": "object", + "properties": { + "decided_at": { + "description": "DecidedAt / DecidedByName / DecidedByShort — when/who decided NSR (may differ from the reporter).", + "type": "string", + "example": "2026-06-15T03:04:05Z" + }, + "decided_by_name": { + "type": "string", + "example": "John Doe" + }, + "decided_by_short": { + "type": "string", + "example": "JDO" + }, + "is_nsr": { + "description": "IsNSR — Non-Safety Release: true = aircraft released to fly despite the open defect.", + "type": "boolean", + "example": false + }, + "reason": { + "description": "Reason — optional justification for the NSR decision.", + "type": "string", + "example": "not required" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "example": "complaint" + } + } + }, + "wucher_internal_transport_http_dto_response.ComplaintResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ComplaintResource" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterAttributes": { + "type": "object", + "properties": { + "bases": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterBaseInfo" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "duty_date": { + "type": "string", + "example": "2026-03-16" + }, + "flight_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterHelicopterSummary" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterDetailsResponse" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterShiftTime" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterBaseGroupAttributes": { + "type": "object", + "properties": { + "bases": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterBaseInfo" + }, + "rosters": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterGroupRoster" + } + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterBaseGroupResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterBaseGroupAttributes" + }, + "type": { + "type": "string", + "example": "hems_base_roster_group" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterBaseInfo": { + "type": "object", + "properties": { + "base_abbreviation": { + "type": "string", + "example": "G1" + }, + "base_name": { + "type": "string", + "example": "Gallus 1 - Zurs Lech" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterShiftTime" + }, + "type": { + "type": "string", + "example": "hems" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterCrew": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "doctor@example.com" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Hunter Wolfgang" + }, + "role": { + "type": "string", + "example": "doctor" + }, + "shift_date": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterShiftDate" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterShiftTime" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterCrewDeleteAttributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + }, + "deleted_count": { + "type": "integer", + "example": 1 + }, + "deleted_items": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterDeletedCrewItem" + } + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterCrewDeleteResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrewDeleteAttributes" + }, + "type": { + "type": "string", + "example": "duty_roster_crew_delete" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterCrewDeleteResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrewDeleteResource" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterDeletedCrewItem": { + "type": "object", + "properties": { + "crew_type": { + "type": "string", + "example": "main" + }, + "date_end": { + "type": "string", + "example": "2026-03-22" + }, + "date_start": { + "type": "string", + "example": "2026-03-20" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "Pilot One" + }, + "role_code": { + "type": "string", + "example": "pilot" + }, + "roster_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "user_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterDetailsResponse": { + "type": "object", + "properties": { + "doctor": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrew" + } + }, + "helper": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrew" + } + }, + "other_person": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrew" + } + }, + "pilot": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterPilotCrew" + } + }, + "rescuer": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterCrew" + } + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterGroupRoster": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "duty_date": { + "type": "string", + "example": "2026-03-16" + }, + "flight_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterHelicopterSummary" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterDetailsResponse" + }, + "shift_time": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterShiftTime" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterGroupedDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterBaseGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 8 + }, + "records_total": { + "type": "integer", + "example": 8 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterGroupedListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterBaseGroupResource" + } + }, + "meta": { + "type": "object", + "properties": { + "from": { + "type": "string", + "example": "2026-03-16" + }, + "to": { + "type": "string", + "example": "2026-04-15" + }, + "total_bases": { + "type": "integer", + "example": 8 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterHelicopterSummary": { + "type": "object", + "properties": { + "designation": { + "type": "string", + "example": "OE-XHZ" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "identifier": { + "type": "string", + "example": "1234" + }, + "type": { + "type": "string", + "example": "EC-135" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterPilotCrew": { + "type": "object", + "properties": { + "co_pilot": { + "type": "boolean" + }, + "crew_type": { + "type": "string", + "example": "main" + }, + "email": { + "type": "string", + "example": "pilot@example.com" + }, + "examiner": { + "type": "boolean" + }, + "flight_instructor": { + "type": "boolean" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "line_checker": { + "type": "boolean" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Hunter Wolfgang" + }, + "role": { + "type": "string", + "example": "pilot" + }, + "shift_date": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterShiftDate" + }, + "supervisor": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "duty_roster" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterResource" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterShiftDate": { + "type": "object", + "properties": { + "date_end": { + "type": "string", + "example": "2026-03-16" + }, + "date_start": { + "type": "string", + "example": "2026-03-16" + } + } + }, + "wucher_internal_transport_http_dto_response.DutyRosterShiftTime": { + "type": "object", + "properties": { + "shift_end": { + "type": "string", + "example": "21:00" + }, + "shift_start": { + "type": "string", + "example": "06:00" + } + } + }, + "wucher_internal_transport_http_dto_response.EASAReleaseAttributes": { + "type": "object", + "properties": { + "complaint_id": { + "type": "string", + "example": "019d6774-1111-7fba-9ef4-3795c0da8a13" + }, + "created_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "easa_ac_hours": { + "type": "string", + "example": "1245.3" + }, + "easa_date": { + "type": "string", + "example": "2026-06-16" + }, + "easa_location": { + "type": "string", + "example": "LOWI" + }, + "easa_maint_manual_rev_airframe": { + "type": "string", + "example": "Rev 12" + }, + "easa_maint_manual_rev_engine": { + "type": "string", + "example": "Rev 8" + }, + "easa_permit_no": { + "type": "string", + "example": "145.A.50" + }, + "easa_signer_id": { + "description": "EASASignerID — contact id (UUIDv7) of the signer/technician.", + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "easa_signer_name": { + "description": "EASASignerName / EASASignerShort — resolved display name + 3-letter short of the signer contact.", + "type": "string", + "example": "John Doe" + }, + "easa_signer_short": { + "type": "string", + "example": "JDO" + }, + "easa_wo_no": { + "type": "string", + "example": "WO-2026-001" + }, + "flight_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "is_signed": { + "description": "IsSigned — true once the release is signed (Release to Service); then the complaint becomes fixed.", + "type": "boolean", + "example": false + }, + "missing_sign_fields": { + "description": "MissingSignFields — required fields still empty; must all be filled before it can be signed.", + "type": "array", + "items": { + "type": "string" + } + }, + "signed_at": { + "description": "SignedAt / SignedBy — when/who signed; empty until signed.", + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "signed_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "updated_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.EASAReleaseDeleteResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "easa_release_delete" + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.EASAReleaseListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResource" + } + }, + "meta": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.EASAReleaseResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "example": "easa_release" + } + } + }, + "wucher_internal_transport_http_dto_response.EASAReleaseResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.EASAReleaseResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerActionBulkError": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "FILE_MANAGER_VALIDATION_FAILED" + }, + "detail": { + "type": "string", + "example": "duplicate name in target folder" + }, + "error_code": { + "type": "string", + "example": "4222111" + }, + "status": { + "type": "integer", + "example": 422 + }, + "title": { + "type": "string", + "example": "Validation error" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerActionLocation": { + "type": "object", + "properties": { + "folder_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerAttachmentListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerAttachmentResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerAttachmentResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerAttachmentAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_attachment" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerAttachmentResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerAttachmentResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerBreadcrumbItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "operations" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileBulkError": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "FILE_MANAGER_VALIDATION_FAILED" + }, + "detail": { + "type": "string", + "example": "duplicate name in target folder" + }, + "error_code": { + "type": "string", + "example": "4222111" + }, + "status": { + "type": "integer", + "example": 422 + }, + "title": { + "type": "string", + "example": "Validation error" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "message": { + "type": "string", + "example": "created" + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileResource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadResource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileCancelUploadResource": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "canceled" + } + } + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_file_cancel_upload_result" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileDetailResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileDetailAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_file" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileDetailResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileDetailResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_file" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_file" + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentResource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileUploadIntentResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileUploadIntentAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_file_upload_intent" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileWOPISessionData": { + "type": "object", + "properties": { + "access_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIs..." + }, + "access_token_ttl": { + "description": "AccessTokenTTL is the access token's absolute expiry time, in epoch\nmilliseconds (equal to the JWT exp claim), as required by WOPI/Collabora.", + "type": "integer", + "example": 1782971996000 + }, + "collabora_action": { + "type": "string", + "example": "https://collabora.example.com/browser/123?WOPISrc=..." + }, + "file_id": { + "type": "string", + "example": "019f0386-0a00-7a4a-a2d8-30afc0e0c3ad" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFileWOPISessionResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFileWOPISessionData" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFolderDetailResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFolderDetailAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_folder" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFolderDetailResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFolderDetailResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFolderResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFolderAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_folder" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerFolderResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerFolderResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerMoveNodeBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerActionBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerMoveNodeResultResource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerMoveNodeBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerMoveNodeBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerMoveNodeResultAttributes": { + "type": "object", + "properties": { + "location": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerActionLocation" + }, + "node_type": { + "type": "string", + "example": "folder" + }, + "status": { + "type": "string", + "example": "moved" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerMoveNodeResultResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerMoveNodeResultAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_move_result" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerNodeAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "extension": { + "type": "string", + "example": "pdf" + }, + "mime_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "operations" + }, + "node_type": { + "type": "string", + "example": "folder" + }, + "parent_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "path_cache": { + "type": "string", + "example": "/root/operations" + }, + "size_bytes": { + "type": "integer", + "example": 1024 + }, + "status": { + "type": "string", + "example": "ready" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerNodeListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerNodeResource" + } + }, + "meta": { + "type": "object", + "properties": { + "breadcrumb": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerBreadcrumbItem" + } + }, + "files": { + "type": "integer", + "example": 8 + }, + "folders": { + "type": "integer", + "example": 4 + }, + "is_root": { + "type": "boolean", + "example": true + }, + "parent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "total": { + "type": "integer", + "example": 12 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerNodeResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerNodeAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_node" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerPurgeNodeBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerActionBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerPurgeNodeResultResource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerPurgeNodeBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerPurgeNodeBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerPurgeNodeResultAttributes": { + "type": "object", + "properties": { + "node_type": { + "type": "string", + "example": "folder" + }, + "status": { + "type": "string", + "example": "purged" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerPurgeNodeResultResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerPurgeNodeResultAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_purge_result" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerTrashListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerTrashNodeResource" + } + }, + "meta": { + "type": "object", + "properties": { + "files": { + "type": "integer", + "example": 70 + }, + "folders": { + "type": "integer", + "example": 50 + }, + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerTrashNodeAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-31T08:52:03Z" + }, + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "extension": { + "type": "string", + "example": "pdf" + }, + "mime_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "Documents" + }, + "node_type": { + "type": "string", + "example": "folder" + }, + "path_cache": { + "type": "string", + "example": "/Documents" + }, + "size_bytes": { + "type": "integer", + "example": 123456 + }, + "trashed_at": { + "type": "string", + "example": "2026-04-01T10:00:00Z" + }, + "updated_at": { + "type": "string", + "example": "2026-03-31T08:52:03Z" + } + } + }, + "wucher_internal_transport_http_dto_response.FileManagerTrashNodeResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FileManagerTrashNodeAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "file_manager_trash_node" + } + } + }, + "wucher_internal_transport_http_dto_response.FleetHistoryEntry": { + "type": "object", + "properties": { + "action": { + "type": "string", + "example": "easa_signed" + }, + "actor": { + "type": "string", + "example": "019e91e6-2908-7f37-93e6-015ed1b7587d" + }, + "actor_name": { + "type": "string", + "example": "John Doe" + }, + "at": { + "type": "string", + "example": "2026-06-17T10:15:00Z" + }, + "entity_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "entity_type": { + "type": "string", + "example": "easa_release" + }, + "message": { + "type": "string", + "example": "Release to Service (EASA signed)" + } + } + }, + "wucher_internal_transport_http_dto_response.FleetHistoryListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetHistoryEntry" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusAttributes": { + "type": "object", + "properties": { + "complaints": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusComplaint" + } + }, + "created_at": { + "type": "string", + "example": "2026-05-29T07:43:06Z" + }, + "created_by": { + "type": "string", + "example": "John Doe" + }, + "easa_release": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusEASARelease" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusFile" + } + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusHelicopter" + }, + "maintenance_schedules": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.MaintenanceScheduleAttributes" + } + }, + "mcf": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusMCF" + }, + "serviced_at": { + "type": "string", + "example": "2026-05-29T07:43:06Z" + }, + "status": { + "type": "string", + "enum": [ + "active", + "serviced" + ], + "example": "active" + }, + "summary": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusSummary" + }, + "updated_at": { + "type": "string", + "example": "2026-05-29T07:43:06Z" + }, + "updated_by": { + "type": "string", + "example": "John Doe" + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 20 + }, + "records_total": { + "type": "integer", + "example": 100 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusDeleteResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "fleet_status_delete" + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusFile": { + "type": "object", + "properties": { + "category": { + "type": "string", + "example": "wb" + }, + "download_url": { + "type": "string", + "example": "https://storage.example.com/fleet/wb.pdf?X-Amz-Signature=abc123" + }, + "file_attachment_id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-111111111111" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "is_mandatory": { + "type": "boolean", + "example": true + }, + "name": { + "type": "string", + "example": "weight_balance.pdf" + }, + "size_bytes": { + "type": "integer", + "example": 1024000 + }, + "thumbnail_download_url": { + "type": "string", + "example": "https://storage.example.com/fleet/wb.thumb.jpg?X-Amz-Signature=abc123" + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 100 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusMarkServicedResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "serviced": { + "type": "boolean", + "example": true + } + } + }, + "type": { + "type": "string", + "example": "fleet_status_mark_serviced" + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusAttributes" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "example": "fleet_status" + } + } + }, + "wucher_internal_transport_http_dto_response.FleetStatusResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FleetStatusResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightAttributes": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "M-062226-001" + }, + "created_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "created_by": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.UserRef" + }, + "date": { + "type": "string", + "example": "2026-04-14" + }, + "deleted_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "deleted_by": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.UserRef" + }, + "draft": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDraftGroup" + } + }, + "mission_id": { + "type": "string", + "example": "019e9640-f7af-7940-80e8-1e9a83013afb" + }, + "status": { + "type": "string", + "example": "draft" + }, + "steps": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightStepItem" + } + }, + "takeover": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightTakeoverSummary" + }, + "total_draft": { + "type": "integer", + "example": 6 + }, + "updated_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "updated_by": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.UserRef" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDailyInspectionSummary": { + "type": "object", + "properties": { + "date": { + "type": "string", + "example": "17.04.2026" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "name": { + "type": "string", + "example": "Ganahl Stefan" + }, + "short_name": { + "type": "string", + "example": "GAN" + }, + "utc_time": { + "type": "string", + "example": "10:57" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataAttributes": { + "type": "object", + "properties": { + "co_pilot": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataCoPilotRef" + }, + "created_at": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "created_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "customer_name": { + "type": "string", + "example": "Air Service" + }, + "delivery_note_number": { + "type": "string", + "example": "DN-1001" + }, + "engine": { + "type": "string", + "example": "PW207D1" + }, + "flight_duration_seconds": { + "type": "integer", + "example": 4500 + }, + "flight_landing": { + "type": "string", + "example": "2026-04-20T09:15:00Z" + }, + "flight_plan_distance": { + "type": "number", + "example": 120.5 + }, + "flight_plan_time_seconds": { + "type": "integer", + "example": 4200 + }, + "flight_plan_true_course": { + "type": "number", + "example": 182.3 + }, + "flight_positioning": { + "type": "boolean", + "example": false + }, + "flight_red_seconds": { + "type": "integer", + "example": 300 + }, + "flight_take_off": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "from": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataLocationRef" + }, + "fuel_after_flight": { + "type": "number", + "example": 280 + }, + "fuel_before_flight": { + "type": "number", + "example": 420 + }, + "fuel_planning": { + "type": "number", + "example": 390 + }, + "fuel_upload": { + "type": "number", + "example": 100 + }, + "hec": { + "allOf": [ + { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHECDetails" + } + ], + "x-note": "Only returned for SPO missions" + }, + "heslo": { + "allOf": [ + { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHESLODetails" + } + ], + "x-note": "Only returned for SPO missions" + }, + "hook_releases": { + "type": "integer", + "example": 0 + }, + "landing_count": { + "type": "integer", + "example": 1 + }, + "logging": { + "allOf": [ + { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOLoggingDetails" + } + ], + "x-note": "Only returned for SPO missions" + }, + "max_n1": { + "type": "number", + "example": 97.5 + }, + "max_n2": { + "type": "number", + "example": 98.1 + }, + "mission": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataMissionRef" + }, + "other_information": { + "type": "string" + }, + "pax_count": { + "type": "integer", + "example": 4 + }, + "roster_details": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterDetailsResponse" + }, + "rotor_brake_cycle": { + "type": "integer", + "example": 1 + }, + "status": { + "type": "string", + "example": "in_progress" + }, + "ticket_no": { + "type": "string", + "example": "TKT-001" + }, + "to": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataLocationRef" + }, + "updated_at": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataCoPilotRef": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "john.doe@example.com" + }, + "first_name": { + "type": "string", + "example": "John" + }, + "id": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "last_name": { + "type": "string", + "example": "Doe" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataLocationRef": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "WIII" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "label": { + "type": "string", + "example": "WIII" + }, + "name": { + "type": "string", + "example": "General Hospital" + }, + "type": { + "type": "string", + "example": "icao" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataMissionRef": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "type": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataAttributes" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "type": { + "type": "string", + "example": "flight_data" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataResource" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHECDetails": { + "type": "object", + "properties": { + "flights": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHECFlight" + } + }, + "loads": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHECLoad" + } + }, + "slings": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHECSling" + } + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHECFlight": { + "type": "object", + "properties": { + "equipment_id": { + "description": "Facility category: hec-rope-label.", + "type": "string" + }, + "equipment_name": { + "type": "string" + }, + "hec_cycle": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "rot": { + "type": "integer" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHECLoad": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "load_category": { + "description": "Weight band for the HEC load, e.g. \"bis 199 kg\", \"200-600 kg\", \"600-800 kg\", \"800-1000 kg\".", + "type": "string", + "example": "200-600 kg" + }, + "quantity": { + "type": "integer", + "example": 3 + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHECSling": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sling_id": { + "description": "Facility category: hec-rope-length.", + "type": "string" + }, + "sling_name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHESLODetails": { + "type": "object", + "properties": { + "flights": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHESLOFlight" + } + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHESLOFlight": { + "type": "object", + "properties": { + "heslo_rope_label_id": { + "description": "Facility category: heslo-rope-label.", + "type": "string" + }, + "heslo_rope_label_name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "rot": { + "type": "integer" + }, + "slings": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOHESLOSling" + } + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOHESLOSling": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sling_id": { + "description": "Facility category: heslo-rope-length.", + "type": "string" + }, + "sling_name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOLoggingDetails": { + "type": "object", + "properties": { + "slings": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataSPOLoggingSling" + } + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDataSPOLoggingSling": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sling_id": { + "description": "Facility category: heslo-hook.", + "type": "string" + }, + "sling_name": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDraftGroup": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDraftItem" + } + }, + "type": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightDraftItem": { + "type": "object", + "properties": { + "flight_data_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a55" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "status": { + "type": "string", + "example": "draft" + }, + "type": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightHelicopterSummary": { + "type": "object", + "properties": { + "aog": { + "type": "boolean", + "example": false + }, + "designation": { + "type": "string", + "example": "AIRBUS H145" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "identifier": { + "type": "string", + "example": "D-HYAR" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "type": { + "type": "string", + "example": "H145" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.FlightMissionTypeSummary": { + "type": "object", + "properties": { + "completed": { + "type": "integer", + "example": 0 + }, + "draft": { + "type": "integer", + "example": 5 + }, + "type": { + "type": "string", + "example": "HEMS" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightAttributes" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "type": { + "type": "string", + "example": "flight" + } + } + }, + "wucher_internal_transport_http_dto_response.FlightStepItem": { + "type": "object", + "properties": { + "complete": { + "type": "boolean", + "example": true + }, + "completed_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "completed_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "mission_type": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightMissionTypeSummary" + } + }, + "phase": { + "type": "string", + "example": "duty_roster" + }, + "position": { + "type": "string", + "example": "takeover" + }, + "status": { + "type": "string", + "example": "completed" + }, + "step": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto_response.FlightTakeoverSummary": { + "type": "object", + "properties": { + "daily_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDailyInspectionSummary" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightHelicopterSummary" + }, + "id": { + "type": "string", + "example": "019e8713-aa19-7fb7-a510-a0332dc69697" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterDetailsResponse" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterAttributes": { + "type": "object", + "properties": { + "aog": { + "type": "boolean", + "example": false + }, + "aog_reason": { + "type": "string", + "example": "Main rotor gearbox fault" + }, + "consumption_lt_min": { + "type": "number", + "example": 4.1 + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterAuditUser" + }, + "designation": { + "type": "string", + "example": "OE-XHZ" + }, + "dry": { + "type": "boolean", + "example": false + }, + "engine_1": { + "type": "string", + "example": "TM" + }, + "engine_2": { + "type": "string", + "example": "TM" + }, + "foto": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterFoto" + }, + "identifier": { + "type": "string", + "example": "ABC-0001" + }, + "igb": { + "type": "boolean", + "example": false + }, + "is_active": { + "type": "boolean", + "example": true + }, + "is_in_use": { + "type": "boolean", + "example": false + }, + "last_daily_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterLastDailyInspection" + }, + "lh": { + "type": "boolean", + "example": true + }, + "maintenance_schedules": { + "description": "MaintenanceSchedules mirrors the helicopter's latest fleet status. It\nalways lists every inspection type; entries without data are empty.", + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.MaintenanceScheduleAttributes" + } + }, + "mgb": { + "type": "boolean", + "example": true + }, + "note": { + "type": "string", + "example": "Primary HEMS helicopter" + }, + "nr1": { + "type": "boolean", + "example": true + }, + "nr2": { + "type": "boolean", + "example": true + }, + "rh": { + "type": "boolean", + "example": true + }, + "sortkey": { + "type": "integer", + "example": 10 + }, + "status": { + "description": "Status is the operational status of the helicopter (read-time derived).\nValues: \"available\", \"booked\", \"aog\", \"mcf\". Precedence: mcf \u003e aog \u003e booked \u003e available.\n\"booked\" = active flight assignment; \"aog\" = air_on_ground flag OR an open complaint\ngrounding it (non-MEL, or MEL past its grace deadline); \"mcf\" = a pending maintenance\ncheck flight (MCF created, not yet passed).", + "type": "string", + "enum": [ + "available", + "booked", + "aog", + "mcf" + ], + "example": "available" + }, + "tgb": { + "type": "boolean", + "example": true + }, + "type": { + "type": "string", + "example": "EC-135" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterAuditUser" + }, + "utc_offset": { + "type": "integer", + "example": -1 + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 12 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileBulkError": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "HELICOPTER_FILE_INVALID_PAYLOAD" + }, + "detail": { + "type": "string", + "example": "helicopter_id is invalid UUID" + }, + "error_code": { + "type": "string", + "example": "4220318" + }, + "status": { + "type": "integer", + "example": 422 + }, + "title": { + "type": "string", + "example": "Validation error" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionAttachment": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "file_name": { + "type": "string", + "example": "Before Flight Checklist.pdf" + }, + "id": { + "type": "string", + "example": "019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionAttributes": { + "type": "object", + "properties": { + "files": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionSections" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionHelicopter" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionHelicopter": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "name": { + "type": "string", + "example": "AIRBUS H145" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionItem": { + "type": "object", + "properties": { + "attachment": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionAttachment" + }, + "attachment_pending": { + "type": "boolean", + "example": true + }, + "created_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "created_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "helicopter_file_id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "is_mandatory": { + "type": "boolean", + "example": true + }, + "is_template": { + "type": "boolean", + "example": true + }, + "position": { + "type": "integer", + "example": 1 + }, + "section": { + "type": "string", + "example": "bfi" + }, + "updated_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionResponse" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionResponse": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionAttributes" + }, + "type": { + "type": "string", + "example": "helicopter_file_collection" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCollectionSections": { + "type": "object", + "properties": { + "after": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionItem" + } + }, + "before": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionItem" + } + }, + "prepare": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCollectionItem" + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCreateBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Resource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileCreateBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileCreateBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileDeleteAttributes": { + "type": "object", + "properties": { + "deleted": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileDeleteResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileDeleteAttributes" + }, + "type": { + "type": "string", + "example": "helicopter_file_delete" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileDeleteResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileDeleteResource" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterFileAttributes" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "example": "helicopter_file" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileResource" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileUpdateBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.Resource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileUpdateBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUpdateBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentBulkItem": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileBulkError" + }, + "index": { + "type": "integer", + "example": 0 + }, + "resource": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentResource" + }, + "success": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentBulkResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentBulkItem" + } + }, + "meta": { + "type": "object", + "properties": { + "failed": { + "type": "integer", + "example": 1 + }, + "partial_success": { + "type": "boolean", + "example": true + }, + "success": { + "type": "integer", + "example": 2 + }, + "total": { + "type": "integer", + "example": 3 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileUploadIntentAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "helicopter_file_upload_intent" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterFileUploadIntentResource" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterNextReportNumberAttributes": { + "type": "object", + "properties": { + "identifier": { + "type": "string", + "example": "ABC" + }, + "report_number": { + "type": "string", + "example": "ABC-0001" + }, + "sequence": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterNextReportNumberResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterNextReportNumberAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "helicopter_report_number" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterNextReportNumberResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterNextReportNumberResource" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "helicopter" + } + } + }, + "wucher_internal_transport_http_dto_response.HelicopterResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.HelicopterResource" + } + } + }, + "wucher_internal_transport_http_dto_response.MCFAttributes": { + "type": "object", + "properties": { + "af_hours": { + "type": "string", + "example": "1245.3" + }, + "completed_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "completed_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "created_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "date": { + "type": "string", + "example": "2026-06-17" + }, + "decided_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "decided_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "flight_id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "is_cleared": { + "type": "boolean", + "example": false + }, + "is_complete": { + "type": "boolean", + "example": false + }, + "is_pending": { + "type": "boolean", + "example": true + }, + "landing_count": { + "type": "integer", + "example": 3 + }, + "notes": { + "type": "string", + "example": "Vibration within limits" + }, + "result": { + "type": "string", + "enum": [ + "passed", + "failed" + ], + "example": "passed" + }, + "updated_at": { + "type": "string", + "example": "2026-06-16T03:04:05Z" + }, + "utc_time": { + "type": "string", + "example": "2026-06-17T08:30:00Z" + } + } + }, + "wucher_internal_transport_http_dto_response.MCFListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFResource" + } + }, + "meta": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.MCFResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "example": "mcf" + } + } + }, + "wucher_internal_transport_http_dto_response.MCFResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MCFResource" + } + } + }, + "wucher_internal_transport_http_dto_response.MasterSettingsAttributes": { + "type": "object", + "properties": { + "company_name": { + "type": "string", + "example": "Wucher" + }, + "cover_file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "cover_original_size_bytes": { + "type": "integer", + "example": 2048000 + }, + "cover_thumbnail_url": { + "type": "string", + "example": "https://cdn.example.com/cover.thumb.png" + }, + "cover_url": { + "type": "string", + "example": "https://cdn.example.com/cover.png" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "logo_file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "logo_original_size_bytes": { + "type": "integer", + "example": 1024000 + }, + "logo_thumbnail_url": { + "type": "string", + "example": "https://cdn.example.com/logo.thumb.png" + }, + "logo_url": { + "type": "string", + "example": "https://cdn.example.com/logo.png" + }, + "subtitle": { + "type": "string", + "example": "Flight operations platform" + }, + "title": { + "type": "string", + "example": "Welcome to Wucher" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraAttributes": { + "type": "object", + "properties": { + "MS_ENTRA_AUTHORITY": { + "type": "string", + "example": "https://login.microsoftonline.com/" + }, + "MS_ENTRA_CLIENT_ID": { + "type": "string", + "example": "your-client-id" + }, + "MS_ENTRA_CLIENT_SECRET": { + "type": "string", + "example": "********" + }, + "MS_ENTRA_REDIRECT_URL": { + "type": "string", + "example": "https://example.com/api/v1/auth/microsoft/callback" + }, + "MS_ENTRA_SCOPES": { + "type": "string", + "example": "openid,profile,email" + }, + "MS_ENTRA_TENANT_ID": { + "type": "string", + "example": "your-tenant-id" + } + } + }, + "wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraAttributes" + }, + "type": { + "type": "string", + "example": "master_settings_microsoft_entra_sso" + } + } + }, + "wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsMicrosoftEntraResource" + } + } + }, + "wucher_internal_transport_http_dto_response.MasterSettingsResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsAttributes" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "master_settings" + } + } + }, + "wucher_internal_transport_http_dto_response.MasterSettingsResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MasterSettingsResource" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "created_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "end_time": { + "type": "string", + "example": "22:05" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionFileItem" + } + }, + "flight": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionFlightRef" + }, + "forms": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionForms" + }, + "mission_code": { + "type": "string", + "example": "HEMS-26-1" + }, + "note": { + "type": "string", + "example": "Mission note" + }, + "start_time": { + "type": "string", + "example": "04:49" + }, + "status": { + "type": "string", + "example": "completed" + }, + "subtypes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionSubtypeRef" + }, + "type": { + "type": "string", + "example": "HEMS" + }, + "updated_at": { + "type": "string", + "example": "2026-04-20T08:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionDataTableResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResource" + } + }, + "meta": { + "type": "object", + "properties": { + "draw": { + "type": "integer", + "example": 1 + }, + "records_filtered": { + "type": "integer", + "example": 42 + }, + "records_total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.MissionFileItem": { + "type": "object", + "properties": { + "attachment_id": { + "type": "string", + "example": "019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e" + }, + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/brief.pdf?X-Amz-Signature=..." + }, + "file_name": { + "type": "string", + "example": "mission-brief.pdf" + }, + "id": { + "type": "string", + "example": "019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e" + }, + "thumbnail_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/thumbnails/2026/04/05/brief.png?X-Amz-Signature=..." + } + } + }, + "wucher_internal_transport_http_dto_response.MissionFlightRef": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "M-062226-001" + }, + "date": { + "type": "string", + "example": "2026-04-20" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionForms": { + "type": "object", + "properties": { + "flight_data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.FlightDataResource" + } + } + } + }, + "wucher_internal_transport_http_dto_response.MissionListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 120 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.MissionResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionAttributes" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "type": { + "type": "string", + "example": "mission" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionResource" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionSubtypeRef": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a99" + }, + "subtype": { + "type": "string", + "example": "PRIM" + }, + "subtype_name": { + "type": "string", + "example": "Nicht transportfaehig" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionTypeAttributes": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "HEMS" + }, + "name": { + "type": "string", + "example": "HEMS" + }, + "subtypes": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionTypeSubtypeItem" + } + } + } + }, + "wucher_internal_transport_http_dto_response.MissionTypeListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionTypeResource" + } + } + } + }, + "wucher_internal_transport_http_dto_response.MissionTypeResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.MissionTypeAttributes" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a77" + }, + "type": { + "type": "string", + "example": "mission_type" + } + } + }, + "wucher_internal_transport_http_dto_response.MissionTypeSubtypeItem": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "PRIM" + }, + "id": { + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a99" + }, + "name": { + "type": "string", + "example": "Primary" + }, + "task_name": { + "type": "string", + "example": "Nicht transportfaehig" + } + } + }, + "wucher_internal_transport_http_dto_response.OtherPersonAttributes": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "guest@example.com" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Guest Person" + } + } + }, + "wucher_internal_transport_http_dto_response.OtherPersonListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonResource" + } + }, + "meta": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.OtherPersonResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonAttributes" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "type": { + "type": "string", + "example": "other_person" + } + } + }, + "wucher_internal_transport_http_dto_response.OtherPersonResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.OtherPersonResource" + } + } + }, + "wucher_internal_transport_http_dto_response.ReserveAcListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcResource" + } + }, + "meta": { + "type": "object", + "properties": { + "page_number": { + "type": "integer", + "example": 1 + }, + "page_size": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 1 + } + } + } + } + }, + "wucher_internal_transport_http_dto_response.ReserveAcResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcAttributes" + }, + "id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "type": { + "type": "string", + "example": "reserve_ac" + } + } + }, + "wucher_internal_transport_http_dto_response.ReserveAcResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.ReserveAcResource" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverDutyRosterCrew": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "doctor@example.com" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Hunter Wolfgang" + }, + "role": { + "type": "string", + "example": "doctor" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverDutyRosterDetailsResponse": { + "type": "object", + "properties": { + "doctor": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterCrew" + } + }, + "helper": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterCrew" + } + }, + "other_person": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterCrew" + } + }, + "pilot": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterPilotCrew" + } + }, + "rescuer": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterCrew" + } + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverDutyRosterPilotCrew": { + "type": "object", + "properties": { + "co_pilot": { + "type": "boolean" + }, + "crew_type": { + "type": "string", + "example": "main" + }, + "email": { + "type": "string", + "example": "pilot@example.com" + }, + "examiner": { + "type": "boolean" + }, + "flight_instructor": { + "type": "boolean" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "line_checker": { + "type": "boolean" + }, + "mobile_phone": { + "type": "string", + "example": "+43-1234" + }, + "name": { + "type": "string", + "example": "Hunter Wolfgang" + }, + "role": { + "type": "string", + "example": "pilot" + }, + "supervisor": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverFileAttributes": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/takeover-report.pdf?X-Amz-Signature=..." + }, + "file_attachment_id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-222222222222" + }, + "file_name": { + "type": "string", + "example": "takeover-report.pdf" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverFileResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverFileAttributes" + }, + "id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-111111111111" + }, + "type": { + "type": "string", + "example": "takeover_file" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverHelicopter": { + "type": "object", + "properties": { + "designation": { + "type": "string", + "example": "OE-XHZ" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "identifier": { + "type": "string", + "example": "1234" + }, + "last_daily_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterLastDailyInspection" + }, + "maintenance_schedules": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.MaintenanceScheduleAttributes" + } + }, + "type": { + "type": "string", + "example": "EC-135" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverResource": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverResponseAttributes" + }, + "type": { + "type": "string", + "example": "takeover" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverResource" + } + } + }, + "wucher_internal_transport_http_dto_response.TakeoverResponseAttributes": { + "type": "object", + "properties": { + "base": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.DutyRosterBaseInfo" + }, + "base_id": { + "type": "string" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "duty_date": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverFileResource" + } + }, + "flight_id": { + "type": "string" + }, + "flight_inspection_id": { + "type": "string" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverHelicopter" + }, + "helicopter_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcInspectionSummary" + }, + "notes": { + "type": "string" + }, + "reserve_ac_id": { + "type": "string" + }, + "roster_detail": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_response.TakeoverDutyRosterDetailsResponse" + }, + "shift_end": { + "type": "string" + }, + "shift_start": { + "type": "string" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_response.UserRef": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Ganahl Stefan" + }, + "uuid": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto_shared.AirRescuerChecklistBaseInfo": { + "type": "object", + "properties": { + "base_abbreviation": { + "type": "string", + "example": "G1" + }, + "base_category": { + "type": "string", + "example": "hems" + }, + "base_name": { + "type": "string", + "example": "Gallus 1 - Zurs Lech" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.AirRescuerChecklistItemInput": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "example": "Oxygen supply check" + }, + "position": { + "type": "integer", + "example": 1 + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerAttachmentAttributes": { + "type": "object", + "properties": { + "attached_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "category": { + "type": "string", + "example": "document" + }, + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "file_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_primary": { + "type": "boolean", + "example": false + }, + "ref_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "ref_type": { + "type": "string", + "example": "helicopter" + }, + "sort_order": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFileAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "extension": { + "type": "string", + "example": "pdf" + }, + "failed_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "failure_reason": { + "type": "string", + "example": "" + }, + "file_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "folder_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "is_template": { + "type": "boolean", + "example": true + }, + "mime_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "policy.pdf" + }, + "name_normalized": { + "type": "string", + "example": "policy.pdf" + }, + "processed_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "processing_started_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "purge_at": { + "type": "string", + "example": "2026-04-12T03:04:05Z" + }, + "size_bytes": { + "type": "integer", + "example": 1024 + }, + "status": { + "type": "string", + "example": "validated" + }, + "trashed_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "upload_error": { + "type": "string", + "example": "" + }, + "validated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFileAuditAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFileDetailAttributes": { + "type": "object", + "properties": { + "audit": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileAuditAttributes" + }, + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "extension": { + "type": "string", + "example": "pdf" + }, + "is_template": { + "type": "boolean", + "example": true + }, + "location": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileLocationAttributes" + }, + "mime_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "policy.pdf" + }, + "name_normalized": { + "type": "string", + "example": "policy.pdf" + }, + "preview_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.preview.pdf?X-Amz-Signature=..." + }, + "processing": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFileProcessingAttributes" + }, + "size_bytes": { + "type": "integer", + "example": 1024 + }, + "status": { + "type": "string", + "example": "ready" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFileLocationAttributes": { + "type": "object", + "properties": { + "folder_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFileProcessingAttributes": { + "type": "object", + "properties": { + "failed_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "failure_reason": { + "type": "string", + "example": "processing validation failed" + }, + "processed_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "processing_started_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "validated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFileUploadIntentAttributes": { + "type": "object", + "properties": { + "expires_at": { + "type": "string", + "example": "2026-04-05T07:29:34Z" + }, + "expires_in_seconds": { + "type": "integer", + "example": 900 + }, + "file": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileUploadFile" + }, + "method": { + "type": "string", + "example": "PUT" + }, + "object": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileUploadObject" + }, + "required_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "upload_intent_uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "upload_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/..." + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFolderAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "deleted_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "deleted_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "depth": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string", + "example": "operations" + }, + "name_normalized": { + "type": "string", + "example": "operations" + }, + "parent_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "path_cache": { + "type": "string", + "example": "/root/operations" + }, + "purge_at": { + "type": "string", + "example": "2026-04-12T03:04:05Z" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFolderAuditAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "created_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "updated_at": { + "type": "string", + "example": "2026-03-12T03:04:05Z" + }, + "updated_by": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFolderDetailAttributes": { + "type": "object", + "properties": { + "audit": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFolderAuditAttributes" + }, + "depth": { + "type": "integer", + "example": 1 + }, + "location": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FileManagerFolderLocationAttributes" + }, + "name": { + "type": "string", + "example": "operations" + }, + "name_normalized": { + "type": "string", + "example": "operations" + }, + "path_cache": { + "type": "string", + "example": "/root/operations" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileManagerFolderLocationAttributes": { + "type": "object", + "properties": { + "parent_id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.FileUploadFile": { + "type": "object", + "properties": { + "mime_type": { + "type": "string", + "example": "application/pdf" + }, + "name": { + "type": "string", + "example": "policy-final.pdf" + }, + "size_bytes": { + "type": "integer", + "example": 1024 + } + } + }, + "wucher_internal_transport_http_dto_shared.FileUploadObject": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "example": "wucher-file-dev" + }, + "key": { + "type": "string", + "example": "files/2026/04/05/018f3a5c7a6f7c2a8d4a4b5b1f6c8e9d.pdf" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusComplaint": { + "type": "object", + "properties": { + "action": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusComplaintAction" + }, + "aircraft_hours_at_report": { + "type": "string" + }, + "description": { + "type": "string" + }, + "flight_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "mel": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusComplaintMEL" + }, + "nsr": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusComplaintNSR" + }, + "reported_at": { + "type": "string" + }, + "reported_by": { + "type": "string" + }, + "reported_by_name": { + "type": "string" + }, + "reported_by_short": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active_non_mel", + "active_mel", + "serviced" + ] + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusComplaintAction": { + "type": "object", + "properties": { + "aircraft_hours_at_fix": { + "type": "string" + }, + "signed_at": { + "type": "string" + }, + "signed_by": { + "type": "string" + }, + "signed_by_name": { + "type": "string" + }, + "signed_by_short": { + "type": "string" + }, + "taken": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusComplaintMEL": { + "type": "object", + "properties": { + "classified_at": { + "type": "string" + }, + "classified_by": { + "type": "string" + }, + "classified_by_name": { + "type": "string" + }, + "classified_by_short": { + "type": "string" + }, + "deadline": { + "type": "string" + }, + "severity": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ] + }, + "severity_label": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusComplaintNSR": { + "type": "object", + "properties": { + "decided_at": { + "type": "string" + }, + "decided_by": { + "type": "string" + }, + "decided_by_name": { + "type": "string" + }, + "decided_by_short": { + "type": "string" + }, + "is_nsr": { + "type": "boolean" + }, + "reason": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusEASARelease": { + "type": "object", + "properties": { + "easa_ac_hours": { + "type": "string" + }, + "easa_date": { + "type": "string" + }, + "easa_location": { + "type": "string" + }, + "easa_maint_manual_rev_airframe": { + "type": "string" + }, + "easa_maint_manual_rev_engine": { + "type": "string" + }, + "easa_permit_no": { + "type": "string" + }, + "easa_signer_id": { + "type": "string" + }, + "easa_wo_no": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_signed": { + "type": "boolean" + }, + "missing_sign_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "signed_at": { + "type": "string" + }, + "signed_by": { + "type": "string" + }, + "signed_by_name": { + "type": "string" + }, + "signed_by_short": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusFileAttributes": { + "type": "object", + "required": [ + "category", + "file_id" + ], + "properties": { + "category": { + "type": "string", + "enum": [ + "wb", + "bfi" + ], + "example": "wb" + }, + "file_id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "is_mandatory": { + "description": "IsMandatory — when true, this file must be checked (is_done) during takeover.", + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusHelicopter": { + "type": "object", + "properties": { + "designation": { + "type": "string", + "example": "AIRBUS H145" + }, + "flight_id": { + "description": "FlightID — id of the aircraft's current active flight (present only when it is in\nuse / booked). The fleet page uses it to create an EASA release scoped to the flight.", + "type": "string", + "example": "019d6774-98e8-7fba-9ef4-3795c0da8a13" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "identifier": { + "type": "string", + "example": "D-HEMS" + }, + "status": { + "type": "string", + "enum": [ + "available", + "booked", + "aog", + "mcf" + ], + "example": "available" + }, + "status_reasons": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusHelicopterReason" + } + }, + "type": { + "type": "string", + "example": "EC-135" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusHelicopterReason": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "manual_aog", + "mel_expired", + "non_mel_pending", + "inspection_overdue" + ] + }, + "complaint_id": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "schedule_id": { + "type": "string" + }, + "since": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusMCF": { + "type": "object", + "properties": { + "active": { + "description": "Active — true when the helicopter is currently in MCF (an open draft, or a signed\nbut failed MCF still needing a re-flight); false when cleared/cancelled/none.", + "type": "boolean" + }, + "af_hours": { + "description": "AFHours / LandingCount present only once the MCF is signed; omitted for none/draft/cancelled.", + "type": "string" + }, + "date": { + "type": "string" + }, + "id": { + "type": "string" + }, + "landing_count": { + "type": "integer" + }, + "sign": { + "description": "Sign — true once the MCF is completed/signed; false while it is still a draft.", + "type": "boolean" + }, + "signed_by": { + "description": "SignedBy / SignedByName / SignedByShort — who signed (completed) the MCF; empty while unsigned.", + "type": "string" + }, + "signed_by_name": { + "type": "string" + }, + "signed_by_short": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusSummary": { + "type": "object", + "properties": { + "airframe": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusSummaryAirframe" + }, + "engine_1": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusSummaryEngine" + }, + "engine_2": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.FleetStatusSummaryEngine" + }, + "flight_reports": { + "type": "number" + }, + "hook_releases": { + "type": "number" + }, + "landings": { + "type": "number" + }, + "rotor_brake_cycles": { + "type": "number" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusSummaryAirframe": { + "type": "object", + "properties": { + "cycles": { + "type": "number" + }, + "hours": { + "type": "number" + } + } + }, + "wucher_internal_transport_http_dto_shared.FleetStatusSummaryEngine": { + "type": "object", + "properties": { + "ccc": { + "type": "number" + }, + "gpc_ng_n1": { + "type": "number" + }, + "hours": { + "type": "number" + }, + "ptc_nf_n2": { + "type": "number" + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterAuditUser": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "name": { + "type": "string", + "example": "John Doe" + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterFileAttributes": { + "type": "object", + "properties": { + "attachment_pending": { + "type": "boolean", + "example": true + }, + "created_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "created_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "file_attachment_id": { + "type": "string", + "example": "019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e" + }, + "file_name": { + "type": "string", + "example": "Before Flight Checklist.pdf" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "helicopter_name": { + "type": "string", + "example": "AIRBUS H145" + }, + "is_mandatory": { + "type": "boolean", + "example": true + }, + "is_template": { + "type": "boolean", + "example": true + }, + "position": { + "type": "integer", + "example": 1 + }, + "section": { + "type": "string", + "example": "bfi" + }, + "updated_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterFoto": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://storage.example.com/helicopter/foto.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026X-Amz-Expires=900\u0026X-Amz-Signature=abc123" + }, + "original_size_bytes": { + "type": "integer", + "example": 1024000 + }, + "thumbnail_download_url": { + "type": "string", + "example": "https://storage.example.com/helicopter/foto.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026X-Amz-Expires=900\u0026X-Amz-Signature=abc123" + }, + "uuid": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterInspectionFilesChecklist": { + "type": "object", + "properties": { + "completed": { + "type": "integer", + "example": 47 + }, + "total": { + "type": "integer", + "example": 47 + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterLastDailyBase": { + "type": "object", + "properties": { + "base_abbreviation": { + "type": "string", + "example": "G1" + }, + "base_name": { + "type": "string", + "example": "Gallus 1 - Zurs Lech" + }, + "id": { + "type": "string", + "example": "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" + }, + "type": { + "type": "string", + "example": "hems" + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterLastDailyInspection": { + "type": "object", + "properties": { + "base": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterLastDailyBase" + }, + "inspected_at": { + "type": "string", + "example": "2026-05-25T07:47:00Z" + }, + "inspection_files_checklist": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterInspectionFilesChecklist" + }, + "inspection_id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "inspector": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.HelicopterLastDailyInspector" + }, + "status": { + "type": "string", + "example": "inspected_cleared" + }, + "utc_time": { + "type": "string", + "example": "07:47" + } + } + }, + "wucher_internal_transport_http_dto_shared.HelicopterLastDailyInspector": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "license_no": { + "type": "string", + "example": "4521" + }, + "name": { + "type": "string", + "example": "Teodora Lesmana" + } + } + }, + "wucher_internal_transport_http_dto_shared.MaintenanceScheduleAttributes": { + "type": "object", + "properties": { + "due": { + "type": "string", + "example": "1200" + }, + "ext": { + "type": "string", + "example": "1300" + }, + "inspection_type": { + "type": "string", + "example": "A/F" + }, + "type": { + "type": "string", + "example": "100H" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcAircraftSummary": { + "type": "object", + "properties": { + "aog": { + "type": "boolean", + "example": false + }, + "designation": { + "type": "string", + "example": "AIRBUS H145" + }, + "id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "identifier": { + "type": "string", + "example": "D-HYAR" + }, + "is_active": { + "type": "boolean", + "example": true + }, + "type": { + "type": "string", + "example": "H145" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcAttributes": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "created_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "deleted_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "deleted_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + }, + "flight_id": { + "type": "string", + "example": "019d6773-ccca-71dd-b9c7-8e6f531deb90" + }, + "helicopter": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcAircraftSummary" + }, + "helicopter_id": { + "type": "string", + "example": "019d6771-5fb5-7337-837f-bc5ed85181a1" + }, + "inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcInspectionSummary" + }, + "last_daily_inspection": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcLastDailyInfo" + }, + "status": { + "type": "string", + "example": "pending" + }, + "updated_at": { + "type": "string", + "example": "2026-04-14T08:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "019d61db-53c6-7280-87ec-e65f205c6d3b" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcBeforeInspectionFields": { + "type": "object", + "properties": { + "fuel_added_amount": { + "type": "number" + }, + "fuel_amount": { + "type": "number" + }, + "fuel_unit": { + "type": "string" + }, + "hydraulic_lh_checked": { + "type": "boolean" + }, + "hydraulic_rh_checked": { + "type": "boolean" + }, + "note": { + "type": "string" + }, + "oil_engine_nr1_checked": { + "type": "boolean" + }, + "oil_engine_nr2_checked": { + "type": "boolean" + }, + "oil_transmission_igb_checked": { + "type": "boolean" + }, + "oil_transmission_mgb_checked": { + "type": "boolean" + }, + "oil_transmission_tgb_checked": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcBeforeSectionDTO": { + "type": "object", + "properties": { + "completed": { + "type": "integer", + "example": 1 + }, + "done_file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO" + } + }, + "fields": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcBeforeInspectionFields" + }, + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO" + } + }, + "total": { + "type": "integer", + "example": 3 + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "example": "https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..." + }, + "file_attachment_id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-222222222222" + }, + "file_name": { + "type": "string", + "example": "Before Flight Checklist.pdf" + }, + "helicopter_file_id": { + "type": "string", + "example": "019d7000-aaaa-7bbb-8ccc-111111111111" + }, + "is_done": { + "type": "boolean", + "example": false + }, + "is_mandatory": { + "type": "boolean", + "example": true + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcFleetStatusFileSectionDTO": { + "type": "object", + "properties": { + "completed": { + "type": "integer", + "example": 1 + }, + "done_file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO" + } + }, + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO" + } + }, + "total": { + "type": "integer", + "example": 2 + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcInspectionSummary": { + "type": "object", + "properties": { + "before": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcBeforeSectionDTO" + }, + "fleet_status_file": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFleetStatusFileSectionDTO" + }, + "id": { + "type": "string", + "example": "019d6772-471c-7b70-b42a-d94020ef61e4" + }, + "inspection_date": { + "type": "string", + "example": "2026-04-15" + }, + "prepare": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcPrepareSectionDTO" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcLastDailyInfo": { + "type": "object", + "properties": { + "date": { + "type": "string", + "example": "17.04.2026" + }, + "name": { + "type": "string", + "example": "Ganahl Stefan" + }, + "short_name": { + "type": "string", + "example": "GAN" + }, + "utc_time": { + "type": "string", + "example": "10:57" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcPrepareFields": { + "type": "object", + "properties": { + "notam_briefing": { + "type": "boolean" + }, + "operational_flight_plan": { + "type": "boolean" + }, + "weather_briefing": { + "type": "boolean" + } + } + }, + "wucher_internal_transport_http_dto_shared.ReserveAcPrepareSectionDTO": { + "type": "object", + "properties": { + "completed": { + "type": "integer", + "example": 1 + }, + "done_file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO" + } + }, + "file_checklist": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcFileChecklistItemDTO" + } + }, + "prepare_fields": { + "$ref": "#/definitions/wucher_internal_transport_http_dto_shared.ReserveAcPrepareFields" + }, + "total": { + "type": "integer", + "example": 3 + } + } + }, + "wucher_internal_transport_http_jsonapi.Document": { + "type": "object", + "properties": { + "data": {}, + "meta": {} + } + }, + "wucher_internal_transport_http_jsonapi.ErrorDocument": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorObject" + } + } + } + }, + "wucher_internal_transport_http_jsonapi.ErrorObject": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "error_code": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/wucher_internal_transport_http_jsonapi.ErrorSource" + }, + "status": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_jsonapi.ErrorSource": { + "type": "object", + "properties": { + "pointer": { + "type": "string" + } + } + }, + "wucher_internal_transport_http_jsonapi.Resource": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "additionalProperties": {} + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "tags": [ + { + "description": "Corrective-action sign-off for a helicopter. After a complaint's action_taken is recorded, it is signed off here (POST /action-signoffs/sign with complaint_id); a fleet-wide sign-off (no complaint_id) is also supported. A signed sign-off is the prerequisite for creating an EASA maintenance release.", + "name": "Complaints - Sign Off" + } + ] +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "/", + Schemes: []string{}, + Title: "Wucher API", + Description: "Wucher backend API (JSON:API).\n\nAuth Flow (Email/Password):\n1) POST /api/v1/auth/register\n2) Check email, open verify link (GET /api/v1/auth/verify-email)\n3) POST /api/v1/auth/login\n- If TOTP enabled: response 202 with challenge_token\n4) POST /api/v1/auth/totp/verify (challenge_token + code) -> cookies\n\nAuth Flow (TOTP setup):\n1) POST /api/v1/auth/totp/setup -> secret + otpauth_url (enabled=false)\n2) POST /api/v1/auth/totp/confirm -> enabled=true\n\nAuth Flow (Microsoft Entra SSO):\n1) GET /api/v1/auth/microsoft/login -> redirect to Microsoft\n2) Microsoft redirects to /api/v1/auth/microsoft/callback\n3) Backend checks existing SSO mapping (no auto-register)\n4) If linked user exists -> set cookies + optional redirect to AUTH_SSO_SUCCESS_REDIRECT\n5) If mapping not linked -> login rejected (401)\n6) Existing authenticated user can link/unlink via /api/v1/auth/sso/link and /api/v1/auth/sso/unlink", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/docs_test.go b/docs/docs_test.go new file mode 100644 index 0000000..6b9a857 --- /dev/null +++ b/docs/docs_test.go @@ -0,0 +1,17 @@ +package docs + +import ( + "testing" + + "github.com/swaggo/swag" +) + +func TestInitRegistersSwaggerSpec(t *testing.T) { + registered := swag.GetSwagger(SwaggerInfo.InstanceName()) + if registered == nil { + t.Fatalf("swagger spec %q was not registered", SwaggerInfo.InstanceName()) + } + if registered.ReadDoc() == "" { + t.Fatalf("registered swagger doc is empty") + } +} diff --git a/docs/duty-roster-migrations.md b/docs/duty-roster-migrations.md new file mode 100644 index 0000000..8ed551b --- /dev/null +++ b/docs/duty-roster-migrations.md @@ -0,0 +1,74 @@ +# Duty Roster Migrations + +Catatan eksekusi 6 file migrasi duty roster di [`migrations/`](../migrations/). + +## Konfigurasi Tool Migrasi + +Tool migrasi yang dipakai project ini adalah `goose` ([Makefile:5](../Makefile#L5)) dengan direktori target: + +```makefile +GOOSE_DIR ?= migrations/goose +``` + +**Konsekuensi:** `make migrate-up` hanya menjalankan file di [`migrations/goose/`](../migrations/goose/). Semua file `.sql` di root [`migrations/`](../migrations/) — termasuk 6 file duty roster — **tidak otomatis dieksekusi**. Mereka berfungsi sebagai script SQL ad-hoc / log historis dan harus dijalankan manual (`mysql < file.sql`) atau dipindahkan ke `migrations/goose/` dengan format `-- +goose Up` / `-- +goose Down`. + +## Urutan Eksekusi (by Timestamp) + +Goose mengurutkan file by filename ascending. Untuk dua file dengan timestamp sama (`20260502`), urutannya alfabetis: `duty` < `purge`. + +``` +1. 20260408_hems_duty_roster_crews_confirm_at.sql +2. 20260429_rename_duty_roster_tables.sql +3. 20260430_normalize_duty_roster_legacy.sql +4. 20260502_duty_roster_permissions_unification.sql +5. 20260502_purge_legacy_hems_duty_roster_permissions.sql +6. 20260504_duty_roster_drop_unique_base_date.sql +``` + +## Efek per File + +| # | File | Efek di DB | +|---|---|---| +| 1 | [`20260408_hems_duty_roster_crews_confirm_at.sql`](../migrations/20260408_hems_duty_roster_crews_confirm_at.sql) | `ALTER TABLE hems_duty_roster_crews ADD COLUMN confirm_at DATETIME NULL` (nama tabel lama). Idempoten via `IF NOT EXISTS`. | +| 2 | [`20260429_rename_duty_roster_tables.sql`](../migrations/20260429_rename_duty_roster_tables.sql) | `RENAME TABLE hems_duty_rosters → duty_rosters` dan `hems_duty_roster_crews → duty_roster_crews`. Pakai guard cek `information_schema.TABLES` jadi idempoten. | +| 3 | [`20260430_normalize_duty_roster_legacy.sql`](../migrations/20260430_normalize_duty_roster_legacy.sql) | (a) Rename tabel **lagi** dengan guard sama (no-op kalau #2 sukses). (b) Rename kolom `hems_base_id → base_id` di `duty_rosters`. (c) Ensure `confirm_at` ada di `duty_roster_crews` baru (dan di tabel lama jika masih ada). (d) Rewire FK `roster_crews.roster_id` & `duty_roster_other_persons.roster_id` supaya reference `duty_rosters` (bukan `hems_duty_rosters`). | +| 4 | [`20260502_duty_roster_permissions_unification.sql`](../migrations/20260502_duty_roster_permissions_unification.sql) | Insert 4 permission canonical: `duty_roster.{create,read,update,delete}` (skip via `WHERE NOT EXISTS`). Grant ke role yang punya legacy `hems_duty_roster.*` ke canonical baru, anti-duplikasi via `LEFT JOIN ... NOT EXISTS`. **Tidak menghapus legacy.** | +| 5 | [`20260502_purge_legacy_hems_duty_roster_permissions.sql`](../migrations/20260502_purge_legacy_hems_duty_roster_permissions.sql) | (a) Re-insert canonical (no-op jika #4 sudah jalan). (b) Re-grant (no-op). (c) `DELETE FROM role_permissions WHERE permission.key IN (legacy)`. (d) `DELETE FROM permissions WHERE key IN (legacy)`. | +| 6 | [`20260504_duty_roster_drop_unique_base_date.sql`](../migrations/20260504_duty_roster_drop_unique_base_date.sql) | `DROP INDEX idx_roster_base_date` (sebelumnya unique). `CREATE INDEX idx_roster_base_date ON duty_rosters(base_id, duty_date)` non-unique. **Konsekuensi aplikasi**: schema tidak lagi mencegah multiple roster di pasangan `(base_id, duty_date)` yang sama — kode handler harus mendukung skenario ini (lihat [`ensureRosterDates`](../internal/transport/http/handlers/duty_roster_handler.go) yang sebelumnya dedup-by-date). | + +## Overlap & Redundansi + +- **#2 ⊂ #3** — File `0430_normalize_legacy` sudah include semua rename dari `0429` dengan guard yang sama. File `0429` secara fungsional redundant; akan no-op jika dijalankan setelah `0430`. +- **#4 ⊂ #5** — File `0502_purge_legacy` adalah superset dari `0502_unification`. Insert + grant identik, lalu tambah `DELETE`. File `0502_unification` redundant. + +## Idempotensi Re-run + +| File | Idempoten? | Catatan | +|---|---|---| +| #1 | Ya | `ADD COLUMN IF NOT EXISTS` | +| #2 | Ya | Guard via `information_schema.TABLES` | +| #3 | Ya | Setiap `ALTER` dibungkus prepared statement dengan kondisi | +| #4 | Ya | `WHERE NOT EXISTS` untuk insert + `LEFT JOIN ... IS NULL` untuk grant | +| #5 | Ya | Sama dengan #4, ditambah `DELETE` (idempoten karena hanya hapus key legacy) | +| **#6** | **TIDAK** | `DROP INDEX idx_roster_base_date` tanpa guard. Re-run setelah index sudah di-drop + recreate (sebagai non-unique) akan **gagal** karena nama indexnya sama. Mestinya pakai prepared statement guard yang cek `information_schema.STATISTICS`. | + +## Versi Minimal yang Setara + +Dengan urutan dijalankan penuh, efek akhirnya cukup dicapai dengan 4 file: + +``` +0408_..._confirm_at (kompat untuk env yang masih punya hems_duty_*) +0430_normalize_legacy (rename tabel + kolom + FK + confirm_at — superset dari 0429) +0502_purge_legacy (canonical perm + delete legacy — superset dari 0502_unification) +0504_drop_unique_base_date (butuh tambah guard agar idempoten) +``` + +Untuk env yang sudah pasti **fresh** (tanpa sisa `hems_duty_*` apa pun), kelima file di atas bisa digabung jadi satu file goose-format dengan branch yang relevan saja. **Sebelum melakukan ini, pastikan tidak ada DB target yang masih nyangkut di mid-state** — file individual lebih aman selama transisi. + +## Bug yang Pernah Ditemukan Akibat #6 + +Setelah `idx_roster_base_date` jadi non-unique, beberapa baris kode aplikasi yang asumsi unique (`base_id`, `duty_date`) jadi salah: + +- [`ensureRosterDates`](../internal/transport/http/handlers/duty_roster_handler.go) di handler GET `/duty-roster/get-all` & `/get-all/dt` sebelumnya pakai `map[string]R` keyed by date — multiple roster di tanggal sama di-overwrite, hanya 1 yang muncul. Sudah diperbaiki pakai `map[string][]R` (multi-value). + +Pastikan setiap kode baru yang query/iterasi roster per `(base_id, duty_date)` menyadari bahwa kombinasi ini tidak lagi unique. diff --git a/docs/error-catalog.md b/docs/error-catalog.md new file mode 100644 index 0000000..40f18b2 --- /dev/null +++ b/docs/error-catalog.md @@ -0,0 +1,735 @@ +# Error Catalog + +Dokumen ini berisi daftar lengkap kontrak error yang dikembalikan backend (`apperrorsx`). Single source of truth ada pada [internal/shared/pkg/apperrorsx/codes_defs.go](../internal/shared/pkg/apperrorsx/codes_defs.go). + + +## Error Code Format + +Format `error_code`: + +```text +[HTTP_STATUS(3)][MODULE_CODE(2)][CASE_CODE(2)] +``` + +Contoh: + +- `4010202` → status `401`, module `02` (PIN), case `02` (PIN blocked) +- `4040302` → status `404`, module `03` (HELICOPTER), case `02` (Helicopter not found) + +## Module Code + +| Code | Module | Konstanta | +|---:|---|---| +| `00` | General | `ModuleGeneral` | +| `01` | Auth | `ModuleAuth` | +| `02` | PIN | `ModulePIN` | +| `03` | Helicopter | `ModuleHelicopter` | +| `04` | User | `ModuleUser` | +| `05` | Master Data | `ModuleMasterData` | +| `06` | Duty Roster | `ModuleDutyRoster` | +| `07` | Flight | `ModuleFlight` | +| `08` | Contact | `ModuleContact` | +| `09` | HEMS / Mission Operations | `ModuleMissionOps` | +| `10` | Facility | `ModuleFacility` | +| `11` | No ICAO Code | `ModuleHospital` | +| `12` | Federal State | `ModuleFederalState` | +| `13` | Nationality | `ModuleNationality` | +| `14` | ICAO | `ModuleICAO` | +| `15` | Country | `ModuleLand` | +| `16` | Base | `ModuleBase` | +| `17` | Health Insurance Companies | `ModuleHealthInsuranceCompanies` | +| `18` | Vocation | `ModuleVocation` | +| `19` | Medicine | `ModuleMedicine` | +| `20` | Master Settings | `ModuleMasterSettings` | +| `21` | File Manager | `ModuleFileManager` | +| `22` | OPC Data | `ModuleOPCData` | +| `23` | Patient Data | `ModulePatientData` | +| `24` | Insurance Patient Data | `ModuleInsurancePatientData` | +| `25` | Role | `ModuleRole` | +| `26` | Flight Data | `ModuleFlightData` | +| `27` | Air Rescuer Checklist | `ModuleAirRescuerChecklist` | +| `28` | Branding | `ModuleBranding` | +| `29` | DUL | `ModuleDUL` | +| `37` | After Flight Inspection | `ModuleAfterFlightInspection` | +| `30` | Takeover | `ModuleTakeover` | +| `31` | Fleet Status | `ModuleFleetStatus` | + +## Catalog + +### General (Module `00`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `INVALID_REQUEST` | `4000001` | 400 | Bad Request | invalid request | +| `DATA_NOT_FOUND` | `4040002` | 404 | Not found | data not found | +| `VALIDATION_ERROR` | `4220003` | 422 | Validation error | validation error | +| `BUSINESS_RULE_FAILED` | `4220004` | 422 | Business rule failed | business rule validation failed | +| `INTERNAL_SERVER_ERROR` | `5000005` | 500 | Internal server error | internal server error | + +### Auth (Module `01`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `SESSION_EXPIRED` | `4010101` | 401 | Unauthorized | session expired | +| `TOKEN_INVALID` | `4010102` | 401 | Unauthorized | token is invalid | +| `USER_UNAUTHORIZED` | `4010103` | 401 | Unauthorized | user is unauthorized | +| `FORBIDDEN_ACCESS` | `4030104` | 403 | Forbidden | forbidden access | +| `AUTH_INVALID_JSON` | `4000105` | 400 | Bad Request | invalid json | +| `AUTH_REGISTRATION_DISABLED` | `4030106` | 403 | Forbidden | registration is disabled | +| `AUTH_ROLE_NOT_FOUND` | `4000107` | 400 | Bad Request | public registration role not found | +| `AUTH_REGISTRATION_FAILED` | `4000108` | 400 | Bad Request | registration failed | +| `AUTH_INVITE_FAILED` | `4000109` | 400 | Bad Request | invite failed | +| `AUTH_SET_PASSWORD_FAILED` | `4000110` | 400 | Bad Request | set password failed | +| `AUTH_SET_PASSWORD_TOKEN_INVALID` | `4000111` | 400 | Bad Request | set password token invalid | +| `AUTH_USERNAME_ALREADY_REGISTERED` | `4000112` | 400 | Bad Request | username already registered | +| `AUTH_RESET_PASSWORD_INVALID_LINK` | `4000113` | 400 | Bad Request | reset password link invalid | +| `AUTH_RESET_PASSWORD_FAILED` | `5000114` | 500 | Internal server error | reset password failed | + +### PIN (Module `02`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `PIN_INCORRECT` | `4010201` | 401 | Unauthorized | invalid security pin | +| `PIN_BLOCKED` | `4230202` | 423 | Locked | PIN has been blocked | +| `PIN_ATTEMPT_LIMIT_REACHED` | `4290203` | 429 | Too many requests | PIN attempt limit reached | +| `PIN_NOT_SET` | `4090204` | 409 | PIN not configured | security PIN is not set | +| `PIN_NOT_BLOCKED` | `4090207` | 409 | PIN not blocked | security PIN is not blocked | +| `PIN_VALIDATION_FAILED` | `4220205` | 422 | Validation error | PIN validation failed | +| `PIN_VERIFICATION_REQUIRED` | `2020206` | 202 | PIN verification required | valid PIN action token is required | + +### Helicopter (Module `03`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `HELICOPTER_DUPLICATE` | `4090301` | 409 | Conflict | helicopter identifier already exists | +| `HELICOPTER_NOT_FOUND` | `4040302` | 404 | Not found | helicopter not found | +| `HELICOPTER_RELATION_DELETE_ERROR` | `4090303` | 409 | Conflict | cannot delete helicopter due to active relations | +| `HELICOPTER_INVALID_PAYLOAD` | `4220304` | 422 | Validation error | invalid helicopter payload | +| `HELICOPTER_INVALID_JSON` | `4000305` | 400 | Bad Request | invalid json payload | +| `HELICOPTER_INVALID_ID` | `4220306` | 422 | Validation error | helicopter id is invalid | +| `HELICOPTER_ID_REQUIRED` | `4220307` | 422 | Validation error | helicopter id is required | +| `HELICOPTER_ID_MISMATCH` | `4220308` | 422 | Validation error | helicopter id mismatch | +| `HELICOPTER_ATTRIBUTES_REQUIRED` | `4220309` | 422 | Validation error | at least one attribute must be provided | +| `HELICOPTER_CREATE_FAILED` | `4000310` | 400 | Bad Request | failed to create helicopter | +| `HELICOPTER_UPDATE_FAILED` | `4000311` | 400 | Bad Request | failed to update helicopter | +| `HELICOPTER_DELETE_FAILED` | `4000312` | 400 | Bad Request | failed to delete helicopter | +| `HELICOPTER_GENERATE_FAILED` | `4000313` | 400 | Bad Request | failed to generate helicopter report number | +| `HELICOPTER_LIST_FAILED` | `4000314` | 400 | Bad Request | failed to list helicopter data | +| `HELICOPTER_PIN_VERIFICATION_REQUIRED` | `2020315` | 202 | PIN verification required | valid PIN action token is required | +| `HELICOPTER_UNAUTHORIZED` | `4010316` | 401 | Unauthorized | helicopter action unauthorized | +| `HELICOPTER_INVALID_TYPE` | `4220317` | 422 | Validation error | helicopter type is invalid | +| `HELICOPTER_FILE_NOT_FOUND` | `4040318` | 404 | Not found | helicopter file not found | +| `HELICOPTER_FILE_INVALID_PAYLOAD` | `4220319` | 422 | Validation error | invalid helicopter file payload | +| `HELICOPTER_FILE_INVALID_JSON` | `4000320` | 400 | Bad Request | invalid json payload | +| `HELICOPTER_FILE_INVALID_UUID` | `4220321` | 422 | Validation error | helicopter file uuid is invalid | +| `HELICOPTER_FILE_SECTION_INVALID` | `4220322` | 422 | Validation error | section is invalid | +| `HELICOPTER_FILE_FILE_NAME_REQUIRED` | `4220323` | 422 | Validation error | file_name is required | +| `HELICOPTER_FILE_CREATE_FAILED` | `4000324` | 400 | Bad Request | failed to create helicopter file | +| `HELICOPTER_FILE_UPDATE_FAILED` | `4000325` | 400 | Bad Request | failed to update helicopter file | +| `HELICOPTER_FILE_DELETE_FAILED` | `4000326` | 400 | Bad Request | failed to delete helicopter file | +| `HELICOPTER_FILE_GET_FAILED` | `4000327` | 400 | Bad Request | failed to get helicopter file | +| `HELICOPTER_FILE_LIST_FAILED` | `4000328` | 400 | Bad Request | failed to list helicopter file data | + +### User (Module `04`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `USER_NOT_FOUND` | `4040401` | 404 | Not found | user not found | +| `USER_INVALID_PAYLOAD` | `4220402` | 422 | Validation error | invalid user payload | +| `USER_INVALID_JSON` | `4000403` | 400 | Bad Request | invalid json payload | +| `USER_INVALID_UUID` | `4220404` | 422 | Validation error | user uuid is invalid | +| `USER_VALIDATION_FAILED` | `4220405` | 422 | Validation error | user validation failed | +| `USER_CREATE_FAILED` | `4000406` | 400 | Bad Request | failed to create user | +| `USER_UPDATE_FAILED` | `4000407` | 400 | Bad Request | failed to update user | +| `USER_DELETE_FAILED` | `4000408` | 400 | Bad Request | failed to delete user | +| `USER_LIST_FAILED` | `4000409` | 400 | Bad Request | failed to list users | +| `USER_TIMEZONE_INVALID` | `4220410` | 422 | Validation error | timezone is invalid | +| `USER_ROLE_IDS_INVALID` | `4220411` | 422 | Validation error | role_ids contains invalid uuid | +| `USER_ROLE_IDS_REQUIRED` | `4220412` | 422 | Validation error | role_ids must contain at least one role | +| `USER_PRIMARY_ROLE_INVALID` | `4220413` | 422 | Validation error | primary role must be included in role_ids | + +### Master Data (Module `05`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `MASTER_DATA_NOT_FOUND` | `4040501` | 404 | Not found | master data not found | +| `MASTER_DATA_INVALID_PAYLOAD` | `4220502` | 422 | Validation error | invalid master data payload | +| `MASTER_DATA_INVALID_JSON` | `4000503` | 400 | Bad Request | invalid json payload | +| `MASTER_DATA_VALIDATION_FAILED` | `4220504` | 422 | Validation error | master data validation failed | +| `MASTER_DATA_CREATE_FAILED` | `4000505` | 400 | Bad Request | failed to create master data | +| `MASTER_DATA_UPDATE_FAILED` | `4000506` | 400 | Bad Request | failed to update master data | +| `MASTER_DATA_DELETE_FAILED` | `4000507` | 400 | Bad Request | failed to delete master data | +| `MASTER_DATA_LIST_FAILED` | `4000508` | 400 | Bad Request | failed to list master data | + +### Duty Roster (Module `06`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `DUTY_ROSTER_NOT_FOUND` | `4040601` | 404 | Not found | duty roster not found | +| `DUTY_ROSTER_INVALID_PAYLOAD` | `4220602` | 422 | Validation error | invalid duty roster payload | +| `DUTY_ROSTER_INVALID_JSON` | `4000603` | 400 | Bad Request | invalid json payload | +| `DUTY_ROSTER_INVALID_UUID` | `4220604` | 422 | Validation error | duty roster uuid is invalid | +| `DUTY_ROSTER_VALIDATION_FAILED` | `4220605` | 422 | Validation error | duty roster validation failed | +| `DUTY_ROSTER_CONFLICT` | `4090606` | 409 | Conflict | duty roster conflict | +| `DUTY_ROSTER_CREATE_FAILED` | `4000607` | 400 | Bad Request | failed to create duty roster | +| `DUTY_ROSTER_UPDATE_FAILED` | `4000608` | 400 | Bad Request | failed to update duty roster | +| `DUTY_ROSTER_DELETE_FAILED` | `4000609` | 400 | Bad Request | failed to delete duty roster | +| `DUTY_ROSTER_LIST_FAILED` | `4000610` | 400 | Bad Request | failed to list duty roster data | +| `DUTY_ROSTER_UNAUTHORIZED` | `4010611` | 401 | Unauthorized | duty roster unauthorized | +| `DUTY_ROSTER_BASE_TYPE_INVALID` | `4220612` | 422 | Validation error | duty roster base type is invalid | +| `DUTY_ROSTER_RELATION_DELETE_ERROR` | `4090613` | 409 | Conflict | cannot delete duty roster due to active relations | + +### DUL (Module `29`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `DUL_NOT_FOUND` | `4042901` | 404 | Not found | dul not found | +| `DUL_INVALID_PAYLOAD` | `4222902` | 422 | Validation error | invalid dul payload | +| `DUL_INVALID_JSON` | `4002903` | 400 | Bad Request | invalid json payload | +| `DUL_INVALID_UUID` | `4222904` | 422 | Validation error | dul uuid is invalid | +| `DUL_VALIDATION_FAILED` | `4222905` | 422 | Validation error | dul validation failed | +| `DUL_CREATE_FAILED` | `4002906` | 400 | Bad Request | failed to create dul | +| `DUL_UPDATE_FAILED` | `4002907` | 400 | Bad Request | failed to update dul | +| `DUL_DELETE_FAILED` | `4002908` | 400 | Bad Request | failed to delete dul | +| `DUL_LIST_FAILED` | `4002909` | 400 | Bad Request | failed to list dul | +| `DUL_DATE_INVALID` | `4222910` | 422 | Validation error | date must be YYYY-MM-DD | + +### Takeover (Module `30`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `TAKEOVER_NOT_FOUND` | `4043001` | 404 | Not found | takeover not found | +| `TAKEOVER_INVALID_PAYLOAD` | `4223002` | 422 | Validation error | invalid takeover payload | +| `TAKEOVER_INVALID_JSON` | `4003003` | 400 | Bad Request | invalid json payload | +| `TAKEOVER_INVALID_UUID` | `4223004` | 422 | Validation error | takeover uuid is invalid | +| `TAKEOVER_ID_REQUIRED` | `4223005` | 422 | Validation error | id is required | +| `TAKEOVER_ID_MISMATCH` | `4223006` | 422 | Validation error | id does not match path uuid | +| `TAKEOVER_FILE_SECTION_INVALID` | `4223007` | 422 | Validation error | helicopter_file_id does not belong to selected helicopter/section | +| `TAKEOVER_CREATE_FAILED` | `4003007` | 400 | Bad Request | failed to create takeover | +| `TAKEOVER_UPDATE_FAILED` | `4003008` | 400 | Bad Request | failed to update takeover | +| `TAKEOVER_DELETE_FAILED` | `4003009` | 400 | Bad Request | failed to delete takeover | +| `TAKEOVER_GET_FAILED` | `4003010` | 400 | Bad Request | failed to get takeover | +| `TAKEOVER_LIST_FAILED` | `4003011` | 400 | Bad Request | failed to list takeover data | +| `TAKEOVER_HELICOPTER_IN_USE` | `4093013` | 409 | Conflict | helicopter is already booked and cannot be used | + +### Fleet Status (Module `31`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FLEET_STATUS_NOT_FOUND` | `4043101` | 404 | Not found | fleet status not found | +| `FLEET_STATUS_INVALID_PAYLOAD` | `4223102` | 422 | Validation error | invalid fleet status payload | +| `FLEET_STATUS_INVALID_JSON` | `4003103` | 400 | Bad Request | invalid json payload | +| `FLEET_STATUS_INVALID_ID` | `4223104` | 422 | Validation error | fleet status id is invalid | +| `FLEET_STATUS_ID_REQUIRED` | `4223105` | 422 | Validation error | id is required | +| `FLEET_STATUS_ID_MISMATCH` | `4223106` | 422 | Validation error | id does not match path id | +| `FLEET_STATUS_HELICOPTER_REQUIRED` | `4223107` | 422 | Validation error | helicopter_id is required | +| `FLEET_STATUS_HELICOPTER_INVALID` | `4223108` | 422 | Validation error | referenced helicopter does not exist | +| `FLEET_STATUS_DUPLICATE` | `4093109` | 409 | Conflict | fleet status already exists | +| `FLEET_STATUS_RELATION_DELETE_ERROR` | `4093110` | 409 | Conflict | cannot delete fleet status due to active relations | +| `FLEET_STATUS_CREATE_FAILED` | `4003111` | 400 | Bad Request | failed to create fleet status | +| `FLEET_STATUS_UPDATE_FAILED` | `4003112` | 400 | Bad Request | failed to update fleet status | +| `FLEET_STATUS_DELETE_FAILED` | `4003113` | 400 | Bad Request | failed to delete fleet status | +| `FLEET_STATUS_LIST_FAILED` | `4003114` | 400 | Bad Request | failed to list fleet status data | +| `FLEET_STATUS_MARK_SERVICED_FAILED` | `4003115` | 400 | Bad Request | failed to mark fleet status serviced | + +### Flight (Module `07`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FLIGHT_NOT_FOUND` | `4040701` | 404 | Not found | flight not found | +| `FLIGHT_INVALID_PAYLOAD` | `4220702` | 422 | Validation error | invalid flight payload | +| `FLIGHT_INVALID_JSON` | `4000703` | 400 | Bad Request | invalid json payload | +| `FLIGHT_INVALID_UUID` | `4220704` | 422 | Validation error | flight uuid is invalid | +| `FLIGHT_VALIDATION_FAILED` | `4220705` | 422 | Validation error | flight validation failed | +| `FLIGHT_CREATE_FAILED` | `4000706` | 400 | Bad Request | failed to create flight | +| `FLIGHT_GET_FAILED` | `4000707` | 400 | Bad Request | failed to get flight | +| `FLIGHT_LIST_FAILED` | `4000708` | 400 | Bad Request | failed to list flight data | +| `FLIGHT_UNAUTHORIZED` | `4010709` | 401 | Unauthorized | flight action unauthorized | +| `FLIGHT_DATE_INVALID` | `4220710` | 422 | Validation error | date must be YYYY-MM-DD | + +### Contact (Module `08`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `CONTACT_NOT_FOUND` | `4040801` | 404 | Not found | contact not found | +| `CONTACT_INVALID_PAYLOAD` | `4220802` | 422 | Validation error | invalid contact payload | +| `CONTACT_INVALID_JSON` | `4000803` | 400 | Bad Request | invalid json payload | +| `CONTACT_INVALID_USER_ID` | `4220804` | 422 | Validation error | user_id is invalid uuid | +| `CONTACT_VALIDATION_FAILED` | `4220805` | 422 | Validation error | contact validation failed | +| `CONTACT_CREATE_FAILED` | `4000806` | 400 | Bad Request | failed to create contact | +| `CONTACT_GET_FAILED` | `4000807` | 400 | Bad Request | failed to get contact | +| `CONTACT_UPDATE_FAILED` | `4000808` | 400 | Bad Request | failed to update contact | +| `CONTACT_USERNAME_CONFLICT` | `4090809` | 409 | Conflict | username already exists | +| `CONTACT_DELETE_FAILED` | `4000810` | 400 | Bad Request | failed to delete contact | +| `CONTACT_UPDATE_STATUS_FAILED` | `4000811` | 400 | Bad Request | failed to update contact status | +| `CONTACT_UPDATE_ROLE_FAILED` | `4000812` | 400 | Bad Request | failed to update contact role | +| `CONTACT_UPDATE_PILOT_CATEGORY_FAILED` | `4000813` | 400 | Bad Request | failed to update contact pilot category | +| `CONTACT_UPDATE_LICENSE_FAILED` | `4000814` | 400 | Bad Request | failed to update contact license | +| `CONTACT_UPDATE_CHIEF_FLAGS_FAILED` | `4000815` | 400 | Bad Request | failed to update contact chief flags | +| `CONTACT_UPDATE_RESPONSIBLE_COMPLAINTS_FAILED` | `4000816` | 400 | Bad Request | failed to update contact responsible complaints | +| `CONTACT_BULK_UPDATE_FAILED` | `4000817` | 400 | Bad Request | failed to bulk update contacts | +| `CONTACT_LIST_FAILED` | `4000818` | 400 | Bad Request | failed to list contacts | + +### HEMS Operation / Mission Ops (Module `09`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `HEMS_OPERATION_NOT_FOUND` | `4040901` | 404 | Not found | hems operation not found | +| `HEMS_OPERATION_INVALID_PAYLOAD` | `4220902` | 422 | Validation error | invalid hems operation payload | +| `HEMS_OPERATION_INVALID_JSON` | `4000903` | 400 | Bad Request | invalid json payload | +| `HEMS_OPERATION_INVALID_UUID` | `4220904` | 422 | Validation error | hems operation uuid is invalid | +| `HEMS_OPERATION_ATTRIBUTES_REQUIRED` | `4220905` | 422 | Validation error | at least one hems operation attribute must be provided | +| `HEMS_OPERATION_DATE_INVALID_RFC3339` | `4220906` | 422 | Validation error | date must be in RFC3339 format | +| `HEMS_OPERATION_RELATION_REFERENCE_INVALID` | `4220907` | 422 | Validation error | relation reference is invalid | +| `HEMS_OPERATION_CREATE_FAILED` | `4000908` | 400 | Bad Request | failed to create hems operation | +| `HEMS_OPERATION_UPDATE_FAILED` | `4000909` | 400 | Bad Request | failed to update hems operation | +| `HEMS_OPERATION_DELETE_FAILED` | `4000910` | 400 | Bad Request | failed to delete hems operation | +| `HEMS_OPERATION_LIST_FAILED` | `4000911` | 400 | Bad Request | failed to list hems operation | + +### Forces Present (Module `09`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FORCES_PRESENT_NOT_FOUND` | `4040912` | 404 | Not found | forces present not found | +| `FORCES_PRESENT_RELATION_DELETE_ERROR` | `4090913` | 409 | Conflict | cannot delete forces present due to active relations | +| `FORCES_PRESENT_INVALID_PAYLOAD` | `4220914` | 422 | Validation error | invalid forces present payload | +| `FORCES_PRESENT_INVALID_JSON` | `4000915` | 400 | Bad Request | invalid json payload | +| `FORCES_PRESENT_INVALID_UUID` | `4220916` | 422 | Validation error | forces present uuid is invalid | +| `FORCES_PRESENT_ID_REQUIRED` | `4220917` | 422 | Validation error | forces present id is required | +| `FORCES_PRESENT_ID_MISMATCH` | `4220918` | 422 | Validation error | forces present id mismatch | +| `FORCES_PRESENT_ATTRIBUTES_REQUIRED` | `4220919` | 422 | Validation error | at least one forces present attribute must be provided | +| `FORCES_PRESENT_NAME_REQUIRED` | `4220920` | 422 | Validation error | forces present name is required | +| `FORCES_PRESENT_CREATE_FAILED` | `4000921` | 400 | Bad Request | failed to create forces present | +| `FORCES_PRESENT_UPDATE_FAILED` | `4000922` | 400 | Bad Request | failed to update forces present | +| `FORCES_PRESENT_DELETE_FAILED` | `4000923` | 400 | Bad Request | failed to delete forces present | +| `FORCES_PRESENT_LIST_FAILED` | `4000924` | 400 | Bad Request | failed to list forces present data | + +### Mission (Module `09`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `MISSION_NOT_FOUND` | `4040925` | 404 | Not found | mission not found | +| `MISSION_INVALID_PAYLOAD` | `4220926` | 422 | Validation error | invalid mission payload | +| `MISSION_INVALID_JSON` | `4000927` | 400 | Bad Request | invalid json payload | +| `MISSION_INVALID_FLIGHT_UUID` | `4220928` | 422 | Validation error | flight uuid is invalid | +| `MISSION_FLIGHT_ID_REQUIRED` | `4220929` | 422 | Validation error | flight_id is required | +| `MISSION_MISSION_ID_REQUIRED` | `4220930` | 422 | Validation error | mission_id is required | +| `MISSION_TYPE_INVALID` | `4220931` | 422 | Validation error | mission type is invalid | +| `MISSION_SUBTYPE_REQUIRED` | `4220932` | 422 | Validation error | subtype_id is required | +| `MISSION_SUBTYPE_INVALID` | `4220933` | 422 | Validation error | subtype_id is invalid | +| `MISSION_SUBTYPE_FORBIDDEN` | `4220934` | 422 | Validation error | subtype_id is forbidden | +| `MISSION_DUPLICATE_FLIGHT` | `4090935` | 409 | Conflict | mission for this flight already exists | +| `MISSION_CREATE_FAILED` | `4000936` | 400 | Bad Request | failed to create mission | +| `MISSION_UPDATE_FAILED` | `4000937` | 400 | Bad Request | failed to update mission | +| `MISSION_DELETE_FAILED` | `4000938` | 400 | Bad Request | failed to delete mission | +| `MISSION_GET_FAILED` | `4000939` | 400 | Bad Request | failed to get mission | +| `MISSION_INVALID_MISSION_UUID` | `4220940` | 422 | Validation error | mission uuid is invalid | +| `MISSION_LIST_FAILED` | `4000941` | 400 | Bad Request | failed to list mission data | +| `MISSION_AFTER_FLIGHT_LOCKED` | `4230942` | 423 | Locked | mission is locked because after flight inspection already exists | + +### Facility (Module `10`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FACILITY_NOT_FOUND` | `4041001` | 404 | Not found | facility not found | +| `FACILITY_RELATION_DELETE_ERROR` | `4091002` | 409 | Conflict | cannot delete facility due to active relations | +| `FACILITY_INVALID_PAYLOAD` | `4221003` | 422 | Validation error | invalid facility payload | +| `FACILITY_INVALID_JSON` | `4001004` | 400 | Bad Request | invalid json payload | +| `FACILITY_INVALID_UUID` | `4221005` | 422 | Validation error | facility uuid is invalid | +| `FACILITY_ATTRIBUTES_REQUIRED` | `4221006` | 422 | Validation error | at least one facility attribute must be provided | +| `FACILITY_CATEGORY_REQUIRED` | `4221007` | 422 | Validation error | facility category is required | +| `FACILITY_NAME_REQUIRED` | `4221008` | 422 | Validation error | facility name is required | +| `FACILITY_TYPE_REQUIRED` | `4221009` | 422 | Validation error | facility type is required | +| `FACILITY_CREATE_FAILED` | `4001010` | 400 | Bad Request | failed to create facility | +| `FACILITY_UPDATE_FAILED` | `4001011` | 400 | Bad Request | failed to update facility | +| `FACILITY_DELETE_FAILED` | `4001012` | 400 | Bad Request | failed to delete facility | +| `FACILITY_LIST_FAILED` | `4001013` | 400 | Bad Request | failed to list facility data | + +### No ICAO Code (Module `11`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `NO_ICAO_CODE_NOT_FOUND` | `4041101` | 404 | Not found | no icao code not found | +| `NO_ICAO_CODE_RELATION_DELETE_ERROR` | `4091102` | 409 | Conflict | cannot delete no icao code due to active relations | +| `NO_ICAO_CODE_INVALID_PAYLOAD` | `4221103` | 422 | Validation error | invalid no icao code payload | +| `NO_ICAO_CODE_INVALID_JSON` | `4001104` | 400 | Bad Request | invalid json payload | +| `NO_ICAO_CODE_INVALID_UUID` | `4221105` | 422 | Validation error | no icao code uuid is invalid | +| `NO_ICAO_CODE_ID_REQUIRED` | `4221106` | 422 | Validation error | no icao code id is required | +| `NO_ICAO_CODE_ID_MISMATCH` | `4221107` | 422 | Validation error | no icao code id mismatch | +| `NO_ICAO_CODE_ATTRIBUTES_REQUIRED` | `4221108` | 422 | Validation error | at least one no icao code attribute must be provided | +| `NO_ICAO_CODE_NAME_REQUIRED` | `4221109` | 422 | Validation error | no icao code name is required | +| `NO_ICAO_CODE_CREATE_FAILED` | `4001110` | 400 | Bad Request | failed to create no icao code | +| `NO_ICAO_CODE_UPDATE_FAILED` | `4001111` | 400 | Bad Request | failed to update no icao code | +| `NO_ICAO_CODE_DELETE_FAILED` | `4001112` | 400 | Bad Request | failed to delete no icao code | +| `NO_ICAO_CODE_LIST_FAILED` | `4001113` | 400 | Bad Request | failed to list no icao code data | + +### Federal State (Module `12`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FEDERAL_STATE_NOT_FOUND` | `4041201` | 404 | Not found | federal state not found | +| `FEDERAL_STATE_RELATION_DELETE_ERROR` | `4091202` | 409 | Conflict | cannot delete federal state due to active relations | +| `FEDERAL_STATE_INVALID_PAYLOAD` | `4221203` | 422 | Validation error | invalid federal state payload | +| `FEDERAL_STATE_INVALID_JSON` | `4001204` | 400 | Bad Request | invalid json payload | +| `FEDERAL_STATE_INVALID_ID` | `4221205` | 422 | Validation error | federal state id is invalid | +| `FEDERAL_STATE_LAND_ID_INVALID` | `4221206` | 422 | Validation error | federal state land_id is invalid | +| `FEDERAL_STATE_ID_REQUIRED` | `4221207` | 422 | Validation error | federal state id is required | +| `FEDERAL_STATE_ID_MISMATCH` | `4221208` | 422 | Validation error | federal state id mismatch | +| `FEDERAL_STATE_ATTRIBUTES_REQUIRED` | `4221209` | 422 | Validation error | at least one federal state attribute must be provided | +| `FEDERAL_STATE_NAME_REQUIRED` | `4221210` | 422 | Validation error | federal state name is required | +| `FEDERAL_STATE_CREATE_FAILED` | `4001211` | 400 | Bad Request | failed to create federal state | +| `FEDERAL_STATE_UPDATE_FAILED` | `4001212` | 400 | Bad Request | failed to update federal state | +| `FEDERAL_STATE_DELETE_FAILED` | `4001213` | 400 | Bad Request | failed to delete federal state | +| `FEDERAL_STATE_LIST_FAILED` | `4001214` | 400 | Bad Request | failed to list federal state data | + +### Nationality (Module `13`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `NATIONALITY_NOT_FOUND` | `4041301` | 404 | Not found | nationality not found | +| `NATIONALITY_RELATION_DELETE_ERROR` | `4091302` | 409 | Conflict | cannot delete nationality due to active relations | +| `NATIONALITY_INVALID_PAYLOAD` | `4221303` | 422 | Validation error | invalid nationality payload | +| `NATIONALITY_INVALID_JSON` | `4001304` | 400 | Bad Request | invalid json payload | +| `NATIONALITY_INVALID_UUID` | `4221305` | 422 | Validation error | nationality uuid is invalid | +| `NATIONALITY_ID_REQUIRED` | `4221306` | 422 | Validation error | nationality id is required | +| `NATIONALITY_ID_MISMATCH` | `4221307` | 422 | Validation error | nationality id mismatch | +| `NATIONALITY_ATTRIBUTES_REQUIRED` | `4221308` | 422 | Validation error | at least one nationality attribute must be provided | +| `NATIONALITY_NAME_REQUIRED` | `4221309` | 422 | Validation error | nationality name is required | +| `NATIONALITY_CREATE_FAILED` | `4001310` | 400 | Bad Request | failed to create nationality | +| `NATIONALITY_UPDATE_FAILED` | `4001311` | 400 | Bad Request | failed to update nationality | +| `NATIONALITY_DELETE_FAILED` | `4001312` | 400 | Bad Request | failed to delete nationality | +| `NATIONALITY_LIST_FAILED` | `4001313` | 400 | Bad Request | failed to list nationality data | + +### ICAO (Module `14`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `ICAO_NOT_FOUND` | `4041401` | 404 | Not found | icao not found | +| `ICAO_RELATION_DELETE_ERROR` | `4091402` | 409 | Conflict | cannot delete icao due to active relations | +| `ICAO_INVALID_PAYLOAD` | `4221403` | 422 | Validation error | invalid icao payload | +| `ICAO_INVALID_JSON` | `4001404` | 400 | Bad Request | invalid json payload | +| `ICAO_INVALID_UUID` | `4221405` | 422 | Validation error | icao uuid is invalid | +| `ICAO_ID_REQUIRED` | `4221406` | 422 | Validation error | icao id is required | +| `ICAO_ID_MISMATCH` | `4221407` | 422 | Validation error | icao id mismatch | +| `ICAO_ATTRIBUTES_REQUIRED` | `4221408` | 422 | Validation error | at least one icao attribute must be provided | +| `ICAO_CODE_REQUIRED` | `4221409` | 422 | Validation error | icao code is required | +| `ICAO_LAND_ID_INVALID` | `4221410` | 422 | Validation error | icao land_id is invalid | +| `ICAO_FEDERAL_STATE_ID_INVALID` | `4221411` | 422 | Validation error | icao federal_state_id is invalid | +| `ICAO_CREATE_FAILED` | `4001412` | 400 | Bad Request | failed to create icao | +| `ICAO_UPDATE_FAILED` | `4001413` | 400 | Bad Request | failed to update icao | +| `ICAO_DELETE_FAILED` | `4001414` | 400 | Bad Request | failed to delete icao | +| `ICAO_LIST_FAILED` | `4001415` | 400 | Bad Request | failed to list icao data | + +### Country (Module `15`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `COUNTRY_NOT_FOUND` | `4041501` | 404 | Not found | country not found | +| `COUNTRY_RELATION_DELETE_ERROR` | `4091502` | 409 | Conflict | cannot delete country due to active relations | +| `COUNTRY_INVALID_PAYLOAD` | `4221503` | 422 | Validation error | invalid country payload | +| `COUNTRY_INVALID_JSON` | `4001504` | 400 | Bad Request | invalid json payload | +| `COUNTRY_INVALID_ID` | `4221505` | 422 | Validation error | country id is invalid | +| `COUNTRY_ID_REQUIRED` | `4221506` | 422 | Validation error | country id is required | +| `COUNTRY_ID_MISMATCH` | `4221507` | 422 | Validation error | country id mismatch | +| `COUNTRY_ATTRIBUTES_REQUIRED` | `4221508` | 422 | Validation error | at least one country attribute must be provided | +| `COUNTRY_NAME_REQUIRED` | `4221509` | 422 | Validation error | country name is required | +| `COUNTRY_ISO_CODE_INVALID` | `4221510` | 422 | Validation error | country iso_code is invalid | +| `COUNTRY_ISO_CODE_DUPLICATE` | `4091511` | 409 | Conflict | country iso_code already exists | +| `COUNTRY_CREATE_FAILED` | `4001512` | 400 | Bad Request | failed to create country | +| `COUNTRY_UPDATE_FAILED` | `4001513` | 400 | Bad Request | failed to update country | +| `COUNTRY_DELETE_FAILED` | `4001514` | 400 | Bad Request | failed to delete country | +| `COUNTRY_LIST_FAILED` | `4001515` | 400 | Bad Request | failed to list country data | + +### Base (Module `16`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `BASE_NOT_FOUND` | `4041601` | 404 | Not found | base not found | +| `BASE_DELETE_CONFLICT` | `4091615` | 409 | Conflict | cannot delete base because it is referenced by another record | +| `BASE_INVALID_PAYLOAD` | `4221603` | 422 | Validation error | invalid base payload | +| `BASE_INVALID_JSON` | `4001604` | 400 | Bad Request | invalid json payload | +| `BASE_INVALID_UUID` | `4221605` | 422 | Validation error | base uuid is invalid | +| `BASE_INVALID_CATEGORY` | `4221606` | 422 | Validation error | base category is invalid | +| `BASE_ATTRIBUTES_REQUIRED` | `4221607` | 422 | Validation error | at least one base attribute must be provided | +| `BASE_NAME_REQUIRED` | `4221608` | 422 | Validation error | base name is required | +| `BASE_DEFAULT_SHIFT_START_REQUIRED` | `4221616` | 422 | Validation error | default_shift_start is required | +| `BASE_DEFAULT_SHIFT_END_REQUIRED` | `4221617` | 422 | Validation error | default_shift_end is required | +| `BASE_CREATE_FAILED` | `4001609` | 400 | Bad Request | failed to create base | +| `BASE_UPDATE_FAILED` | `4001610` | 400 | Bad Request | failed to update base | +| `BASE_DELETE_FAILED` | `4001611` | 400 | Bad Request | failed to delete base | +| `BASE_LIST_FAILED` | `4001612` | 400 | Bad Request | failed to list base data | +| `BASE_INVALID_CONTACT_IDS` | `4221613` | 422 | Validation error | base contact ids are invalid | +| `BASE_INVALID_TIME` | `4221614` | 422 | Validation error | base time format is invalid | + +Catatan: +- `BASE_DELETE_CONFLICT` mengembalikan detail relasi yang masih mengacu ke base, misalnya `takeover`, `dul`, `base operational shift times`, atau `base contact roles`. +- Detail pesan bisa berubah sesuai tabel relasi yang ditemukan saat delete. + +### Health Insurance Companies (Module `17`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `HEALTH_INSURANCE_COMPANY_NOT_FOUND` | `4041701` | 404 | Not found | health insurance company not found | +| `HEALTH_INSURANCE_COMPANY_RELATION_DELETE_ERROR` | `4091702` | 409 | Conflict | cannot delete health insurance company due to active relations | +| `HEALTH_INSURANCE_COMPANY_INVALID_PAYLOAD` | `4221703` | 422 | Validation error | invalid health insurance company payload | +| `HEALTH_INSURANCE_COMPANY_INVALID_JSON` | `4001704` | 400 | Bad Request | invalid json payload | +| `HEALTH_INSURANCE_COMPANY_INVALID_UUID` | `4221705` | 422 | Validation error | health insurance company uuid is invalid | +| `HEALTH_INSURANCE_COMPANY_ID_REQUIRED` | `4221706` | 422 | Validation error | health insurance company id is required | +| `HEALTH_INSURANCE_COMPANY_ID_MISMATCH` | `4221707` | 422 | Validation error | health insurance company id mismatch | +| `HEALTH_INSURANCE_COMPANY_ATTRIBUTES_REQUIRED` | `4221708` | 422 | Validation error | at least one health insurance company attribute must be provided | +| `HEALTH_INSURANCE_COMPANY_NAME_REQUIRED` | `4221709` | 422 | Validation error | health insurance company name is required | +| `HEALTH_INSURANCE_COMPANY_LAND_ID_INVALID` | `4221710` | 422 | Validation error | health insurance company land_id is invalid | +| `HEALTH_INSURANCE_COMPANY_CREATE_FAILED` | `4001711` | 400 | Bad Request | failed to create health insurance company | +| `HEALTH_INSURANCE_COMPANY_UPDATE_FAILED` | `4001712` | 400 | Bad Request | failed to update health insurance company | +| `HEALTH_INSURANCE_COMPANY_DELETE_FAILED` | `4001713` | 400 | Bad Request | failed to delete health insurance company | +| `HEALTH_INSURANCE_COMPANY_LIST_FAILED` | `4001714` | 400 | Bad Request | failed to list health insurance company data | + +### Vocation (Module `18`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `VOCATION_NOT_FOUND` | `4041801` | 404 | Not found | vocation not found | +| `VOCATION_RELATION_DELETE_ERROR` | `4091802` | 409 | Conflict | cannot delete vocation due to active relations | +| `VOCATION_INVALID_PAYLOAD` | `4221803` | 422 | Validation error | invalid vocation payload | +| `VOCATION_INVALID_JSON` | `4001804` | 400 | Bad Request | invalid json payload | +| `VOCATION_INVALID_UUID` | `4221805` | 422 | Validation error | vocation uuid is invalid | +| `VOCATION_ID_REQUIRED` | `4221806` | 422 | Validation error | vocation id is required | +| `VOCATION_ID_MISMATCH` | `4221807` | 422 | Validation error | vocation id mismatch | +| `VOCATION_ATTRIBUTES_REQUIRED` | `4221808` | 422 | Validation error | at least one vocation attribute must be provided | +| `VOCATION_NAME_REQUIRED` | `4221809` | 422 | Validation error | vocation name is required | +| `VOCATION_CREATE_FAILED` | `4001810` | 400 | Bad Request | failed to create vocation | +| `VOCATION_UPDATE_FAILED` | `4001811` | 400 | Bad Request | failed to update vocation | +| `VOCATION_DELETE_FAILED` | `4001812` | 400 | Bad Request | failed to delete vocation | +| `VOCATION_LIST_FAILED` | `4001813` | 400 | Bad Request | failed to list vocation data | + +### Medicine (Module `19`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `MEDICINE_NOT_FOUND` | `4041901` | 404 | Not found | medicine not found | +| `MEDICINE_RELATION_DELETE_ERROR` | `4091902` | 409 | Conflict | cannot delete medicine due to active relations | +| `MEDICINE_INVALID_PAYLOAD` | `4221903` | 422 | Validation error | invalid medicine payload | +| `MEDICINE_INVALID_JSON` | `4001904` | 400 | Bad Request | invalid json payload | +| `MEDICINE_INVALID_UUID` | `4221905` | 422 | Validation error | medicine uuid is invalid | +| `MEDICINE_ID_REQUIRED` | `4221906` | 422 | Validation error | medicine id is required | +| `MEDICINE_ID_MISMATCH` | `4221907` | 422 | Validation error | medicine id mismatch | +| `MEDICINE_ATTRIBUTES_REQUIRED` | `4221908` | 422 | Validation error | at least one medicine attribute must be provided | +| `MEDICINE_NAME_REQUIRED` | `4221909` | 422 | Validation error | medicine name is required | +| `MEDICINE_GROUP_ID_INVALID` | `4221910` | 422 | Validation error | medicine_group_id is invalid | +| `MEDICINE_MOTOR_REACTION_ID_INVALID` | `4221911` | 422 | Validation error | motor_reaction_id is invalid | +| `MEDICINE_MOTOR_REACTION_IDS_INVALID` | `4221912` | 422 | Validation error | motor_reaction_ids contains invalid uuid | +| `MEDICINE_CREATE_FAILED` | `4001913` | 400 | Bad Request | failed to create medicine | +| `MEDICINE_UPDATE_FAILED` | `4001914` | 400 | Bad Request | failed to update medicine | +| `MEDICINE_DELETE_FAILED` | `4001915` | 400 | Bad Request | failed to delete medicine | +| `MEDICINE_LIST_FAILED` | `4001916` | 400 | Bad Request | failed to list medicine data | + +### Master Settings (Module `20`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `MASTER_SETTINGS_NOT_FOUND` | `4042001` | 404 | Not found | master settings not found | +| `MASTER_SETTINGS_INVALID_PAYLOAD` | `4222002` | 422 | Validation error | invalid master settings payload | +| `MASTER_SETTINGS_INVALID_JSON` | `4002003` | 400 | Bad Request | invalid json payload | +| `MASTER_SETTINGS_INVALID_UUID` | `4222004` | 422 | Validation error | master settings uuid is invalid | +| `MASTER_SETTINGS_ID_REQUIRED` | `4222005` | 422 | Validation error | master settings id is required | +| `MASTER_SETTINGS_ID_MISMATCH` | `4222006` | 422 | Validation error | master settings id mismatch | +| `MASTER_SETTINGS_ATTRIBUTES_REQUIRED` | `4222007` | 422 | Validation error | at least one master settings attribute must be provided | +| `MASTER_SETTINGS_COMPANY_NAME_REQUIRED` | `4222008` | 422 | Validation error | company_name is required | +| `MASTER_SETTINGS_LOGO_FILE_INVALID` | `4222009` | 422 | Validation error | logo file uuid is invalid | +| `MASTER_SETTINGS_COVER_FILE_INVALID` | `4222010` | 422 | Validation error | cover file uuid is invalid | +| `MASTER_SETTINGS_CREATE_FAILED` | `4002011` | 400 | Bad Request | failed to create master settings | +| `MASTER_SETTINGS_UPDATE_FAILED` | `4002012` | 400 | Bad Request | failed to update master settings | +| `MASTER_SETTINGS_DELETE_FAILED` | `4002013` | 400 | Bad Request | failed to delete master settings | +| `MASTER_SETTINGS_GET_FAILED` | `4002014` | 400 | Bad Request | failed to get master settings | +| `MASTER_SETTINGS_MICROSOFT_ENTRA_UPSERT_FAILED` | `4002015` | 400 | Bad Request | failed to upsert microsoft entra sso settings | + +### Branding (Module `28`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `BRANDING_LOGO_FETCH_FAILED` | `4002801` | 400 | Bad Request | failed to fetch branding logo | +| `BRANDING_COVER_FETCH_FAILED` | `4002802` | 400 | Bad Request | failed to fetch branding cover | +| `BRANDING_EMAIL_LOGO_FETCH_FAILED` | `4002803` | 400 | Bad Request | failed to fetch branding email logo | +| `BRANDING_CONTENT_FETCH_FAILED` | `4002804` | 400 | Bad Request | failed to fetch branding content | +| `BRANDING_STORAGE_UNAVAILABLE` | `5032805` | 503 | Internal server error | branding storage service unavailable | +| `BRANDING_FILE_MANAGER_UNAVAILABLE` | `5032806` | 503 | Internal server error | branding file manager service unavailable | + +### File Manager (Module `21`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FILE_MANAGER_STORAGE_UNAVAILABLE` | `5032101` | 503 | Storage unavailable | storage service is unavailable | +| `FILE_MANAGER_QUEUE_UNAVAILABLE` | `5032102` | 503 | Queue unavailable | queue service is unavailable | +| `FILE_MANAGER_UPLOAD_INTENT_UNAVAILABLE` | `5032103` | 503 | Upload intent unavailable | upload intent service is unavailable | +| `FILE_MANAGER_ATTACHMENT_SERVICE_UNAVAILABLE` | `5032104` | 503 | Attachment service unavailable | attachment service is unavailable | +| `FILE_MANAGER_FOLDER_NOT_FOUND` | `4042105` | 404 | Not found | folder not found | +| `FILE_MANAGER_FILE_NOT_FOUND` | `4042106` | 404 | Not found | file not found | +| `FILE_MANAGER_ATTACHMENT_NOT_FOUND` | `4042107` | 404 | Not found | attachment not found | +| `FILE_MANAGER_UPLOAD_INTENT_NOT_FOUND` | `4042108` | 404 | Not found | upload intent not found | +| `FILE_MANAGER_STATUS_CONFLICT` | `4092109` | 409 | Conflict | file status conflict | +| `FILE_MANAGER_UPLOAD_INTENT_CONFLICT` | `4092110` | 409 | Conflict | upload intent conflict | +| `FILE_MANAGER_VALIDATION_FAILED` | `4222111` | 422 | Validation error | file manager validation failed | +| `FILE_MANAGER_ACTION_FAILED` | `4002112` | 400 | Bad Request | file manager action failed | + +### OPC Data (Module `22`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `OPC_NOT_FOUND` | `4042201` | 404 | Not found | opc not found | +| `OPC_RELATION_DELETE_ERROR` | `4092202` | 409 | Conflict | cannot delete opc due to active relations | +| `OPC_INVALID_PAYLOAD` | `4222203` | 422 | Validation error | invalid opc payload | +| `OPC_INVALID_JSON` | `4002204` | 400 | Bad Request | invalid json payload | +| `OPC_INVALID_UUID` | `4222205` | 422 | Validation error | opc uuid is invalid | +| `OPC_ID_REQUIRED` | `4222206` | 422 | Validation error | opc id is required | +| `OPC_ID_MISMATCH` | `4222207` | 422 | Validation error | opc id mismatch | +| `OPC_ATTRIBUTES_REQUIRED` | `4222208` | 422 | Validation error | at least one opc attribute must be provided | +| `OPC_TITLE_REQUIRED` | `4222209` | 422 | Validation error | opc title is required | +| `OPC_CREATE_FAILED` | `4002210` | 400 | Bad Request | failed to create opc | +| `OPC_UPDATE_FAILED` | `4002211` | 400 | Bad Request | failed to update opc | +| `OPC_DELETE_FAILED` | `4002212` | 400 | Bad Request | failed to delete opc | +| `OPC_LIST_FAILED` | `4002213` | 400 | Bad Request | failed to list opc data | + +### Patient Data (Module `23`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `PATIENT_DATA_NOT_FOUND` | `4042301` | 404 | Not found | patient data not found | +| `PATIENT_DATA_RELATION_DELETE_ERROR` | `4092302` | 409 | Conflict | cannot delete patient data due to active relations | +| `PATIENT_DATA_INVALID_PAYLOAD` | `4222303` | 422 | Validation error | invalid patient data payload | +| `PATIENT_DATA_INVALID_JSON` | `4002304` | 400 | Bad Request | invalid json payload | +| `PATIENT_DATA_INVALID_UUID` | `4222305` | 422 | Validation error | patient data uuid is invalid | +| `PATIENT_DATA_ID_REQUIRED` | `4222306` | 422 | Validation error | patient data id is required | +| `PATIENT_DATA_ID_MISMATCH` | `4222307` | 422 | Validation error | patient data id mismatch | +| `PATIENT_DATA_ATTRIBUTES_REQUIRED` | `4222308` | 422 | Validation error | at least one patient data attribute must be provided | +| `PATIENT_DATA_FAMILY_NAME_REQUIRED` | `4222309` | 422 | Validation error | family_name is required | +| `PATIENT_DATA_FIRST_NAME_REQUIRED` | `4222310` | 422 | Validation error | first_name is required | +| `PATIENT_DATA_BIRTH_DATE_INVALID` | `4222311` | 422 | Validation error | birth_date is invalid | +| `PATIENT_DATA_GENDER_INVALID` | `4222312` | 422 | Validation error | gender is invalid | +| `PATIENT_DATA_OPC_ID_INVALID` | `4222313` | 422 | Validation error | opc_id is invalid | +| `PATIENT_DATA_INSURANCE_PATIENT_DATA_ID_INVALID` | `4222314` | 422 | Validation error | insurance_patient_data_id is invalid | +| `PATIENT_DATA_CREATE_FAILED` | `4002315` | 400 | Bad Request | failed to create patient data | +| `PATIENT_DATA_UPDATE_FAILED` | `4002316` | 400 | Bad Request | failed to update patient data | +| `PATIENT_DATA_DELETE_FAILED` | `4002317` | 400 | Bad Request | failed to delete patient data | +| `PATIENT_DATA_LIST_FAILED` | `4002318` | 400 | Bad Request | failed to list patient data | + +### Insurance Patient Data (Module `24`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `INSURANCE_PATIENT_DATA_NOT_FOUND` | `4042401` | 404 | Not found | insurance patient data not found | +| `INSURANCE_PATIENT_DATA_RELATION_DELETE_ERROR` | `4092402` | 409 | Conflict | cannot delete insurance patient data due to active relations | +| `INSURANCE_PATIENT_DATA_INVALID_PAYLOAD` | `4222403` | 422 | Validation error | invalid insurance patient data payload | +| `INSURANCE_PATIENT_DATA_INVALID_JSON` | `4002404` | 400 | Bad Request | invalid json payload | +| `INSURANCE_PATIENT_DATA_INVALID_UUID` | `4222405` | 422 | Validation error | insurance patient data uuid is invalid | +| `INSURANCE_PATIENT_DATA_ID_REQUIRED` | `4222406` | 422 | Validation error | insurance patient data id is required | +| `INSURANCE_PATIENT_DATA_ID_MISMATCH` | `4222407` | 422 | Validation error | insurance patient data id mismatch | +| `INSURANCE_PATIENT_DATA_ATTRIBUTES_REQUIRED` | `4222408` | 422 | Validation error | at least one insurance patient data attribute must be provided | +| `INSURANCE_PATIENT_DATA_HEALTH_INSURANCE_ID_INVALID` | `4222409` | 422 | Validation error | health_insurance_companies_id is invalid | +| `INSURANCE_PATIENT_DATA_FEDERAL_STATE_ID_INVALID` | `4222410` | 422 | Validation error | federal_state_id is invalid | +| `INSURANCE_PATIENT_DATA_CREATE_FAILED` | `4002411` | 400 | Bad Request | failed to create insurance patient data | +| `INSURANCE_PATIENT_DATA_UPDATE_FAILED` | `4002412` | 400 | Bad Request | failed to update insurance patient data | +| `INSURANCE_PATIENT_DATA_DELETE_FAILED` | `4002413` | 400 | Bad Request | failed to delete insurance patient data | +| `INSURANCE_PATIENT_DATA_LIST_FAILED` | `4002414` | 400 | Bad Request | failed to list insurance patient data | + +### Role (Module `25`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `ROLE_NOT_FOUND` | `4042501` | 404 | Not found | role not found | +| `ROLE_PERMISSION_NOT_FOUND` | `4042502` | 404 | Not found | permission not found | +| `ROLE_RELATION_DELETE_ERROR` | `4092503` | 409 | Conflict | cannot delete role due to active relations | +| `ROLE_INVALID_PAYLOAD` | `4222504` | 422 | Validation error | invalid role payload | +| `ROLE_INVALID_JSON` | `4002505` | 400 | Bad Request | invalid json payload | +| `ROLE_INVALID_UUID` | `4222506` | 422 | Validation error | role uuid is invalid | +| `ROLE_INVALID_PERMISSION_UUID` | `4222507` | 422 | Validation error | permission uuid is invalid | +| `ROLE_ID_REQUIRED` | `4222508` | 422 | Validation error | role id is required | +| `ROLE_ID_MISMATCH` | `4222509` | 422 | Validation error | role id mismatch | +| `ROLE_ATTRIBUTES_REQUIRED` | `4222510` | 422 | Validation error | at least one role attribute must be provided | +| `ROLE_NAME_REQUIRED` | `4222511` | 422 | Validation error | role name is required | +| `ROLE_PERMISSION_ID_REQUIRED` | `4222512` | 422 | Validation error | permission_id is required | +| `ROLE_PERMISSION_IDS_EMPTY` | `4222513` | 422 | Validation error | permission_ids must not be empty | +| `ROLE_CREATE_FAILED` | `4002514` | 400 | Bad Request | failed to create role | +| `ROLE_UPDATE_FAILED` | `4002515` | 400 | Bad Request | failed to update role | +| `ROLE_DELETE_FAILED` | `4002516` | 400 | Bad Request | failed to delete role | +| `ROLE_ASSIGN_FAILED` | `4002517` | 400 | Bad Request | failed to assign permission | +| `ROLE_REMOVE_FAILED` | `4002518` | 400 | Bad Request | failed to remove permission | +| `ROLE_LIST_FAILED` | `4002519` | 400 | Bad Request | failed to list role data | +| `ROLE_PERMISSION_LIST_FAILED` | `4002520` | 400 | Bad Request | failed to list permission data | +| `ROLE_PERMISSION_UPDATE_FAILED` | `4002521` | 400 | Bad Request | failed to update permission | +| `ROLE_PERMISSION_REQUIRES_PIN_REQUIRED` | `4222522` | 422 | Validation error | requires_pin is required | + +### Flight Data (Module `26`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `FLIGHT_DATA_NOT_FOUND` | `4042601` | 404 | Not found | flight data not found | +| `FLIGHT_DATA_INVALID_PAYLOAD` | `4222602` | 422 | Validation error | invalid flight data payload | +| `FLIGHT_DATA_INVALID_JSON` | `4002603` | 400 | Bad Request | invalid json payload | +| `FLIGHT_DATA_INVALID_PATH_UUID` | `4222604` | 422 | Validation error | path uuid is invalid | +| `FLIGHT_DATA_MISSION_ID_REQUIRED` | `4222605` | 422 | Validation error | mission_id is required | +| `FLIGHT_DATA_PILOT_ID_REQUIRED` | `4222606` | 422 | Validation error | pilot_id is required | +| `FLIGHT_DATA_INVALID_UUID` | `4222607` | 422 | Validation error | uuid is invalid | +| `FLIGHT_DATA_INVALID_RFC3339` | `4222608` | 422 | Validation error | datetime must be RFC3339 | +| `FLIGHT_DATA_ORIGIN_LOCATION_REFERENCE` | `4222609` | 422 | Validation error | origin location reference is invalid | +| `FLIGHT_DATA_DESTINATION_LOCATION_REFERENCE` | `4222610` | 422 | Validation error | destination location reference is invalid | +| `FLIGHT_DATA_REQUIRED` | `4002611` | 400 | Bad Request | required flight data is missing | +| `FLIGHT_DATA_CREATE_FAILED` | `4002612` | 400 | Bad Request | failed to create flight data | +| `FLIGHT_DATA_GET_FAILED` | `4002613` | 400 | Bad Request | failed to get flight data | +| `FLIGHT_DATA_AFTER_FLIGHT_INCOMPLETE` | `4222616` | 422 | Validation error | cannot submit after-flight: flight data is incomplete | +| `FLIGHT_DATA_CREATE_BLOCKED` | `4092618` | 409 | Conflict | flight data cannot be created because fm report already exists for this flight | +| `FLIGHT_DATA_AFTER_FLIGHT_LOCKED` | `4232618` | 423 | Locked | flight data is locked because after flight inspection already exists | +| `FLIGHT_DATA_LOCKED` | `4232619` | 423 | Locked | flight data is locked because fm report is completed | + +### Air Rescuer Checklist (Module `27`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `AIR_RESCUER_CHECKLIST_NOT_FOUND` | `4042701` | 404 | Not found | air rescuer checklist not found | +| `AIR_RESCUER_CHECKLIST_ITEM_NOT_FOUND` | `4042702` | 404 | Not found | air rescuer checklist item not found | +| `AIR_RESCUER_CHECKLIST_INVALID_PAYLOAD` | `4222703` | 422 | Validation error | invalid air rescuer checklist payload | +| `AIR_RESCUER_CHECKLIST_INVALID_JSON` | `4002704` | 400 | Bad Request | invalid json payload | +| `AIR_RESCUER_CHECKLIST_INVALID_UUID` | `4222705` | 422 | Validation error | air rescuer checklist uuid is invalid | +| `AIR_RESCUER_CHECKLIST_ID_MISMATCH` | `4222706` | 422 | Validation error | id does not match path uuid | +| `AIR_RESCUER_CHECKLIST_SCOPE_INVALID` | `4222707` | 422 | Validation error | scope_code is invalid | +| `AIR_RESCUER_CHECKLIST_SCOPE_REQUIRED` | `4222708` | 422 | Validation error | scope_code is required | +| `AIR_RESCUER_CHECKLIST_TITLE_REQUIRED` | `4222709` | 422 | Validation error | title is required | +| `AIR_RESCUER_CHECKLIST_NAME_REQUIRED` | `4222710` | 422 | Validation error | name is required | +| `AIR_RESCUER_CHECKLIST_BASE_NOT_FOUND` | `4042711` | 404 | Not found | base not found | +| `AIR_RESCUER_CHECKLIST_CREATE_FAILED` | `4002712` | 400 | Bad Request | failed to create air rescuer checklist | +| `AIR_RESCUER_CHECKLIST_UPDATE_FAILED` | `4002713` | 400 | Bad Request | failed to update air rescuer checklist | +| `AIR_RESCUER_CHECKLIST_DELETE_FAILED` | `4002714` | 400 | Bad Request | failed to delete air rescuer checklist | +| `AIR_RESCUER_CHECKLIST_LIST_FAILED` | `4002715` | 400 | Bad Request | failed to list air rescuer checklist | +| `AIR_RESCUER_CHECKLIST_REORDER_FAILED` | `4002716` | 400 | Bad Request | failed to reorder air rescuer checklist items | +| `AIR_RESCUER_CHECKLIST_VALIDATION_FAILED` | `4222717` | 422 | Validation error | air rescuer checklist validation failed | + +### After Flight Inspection (Module `37`) + +| Code | Error Code | HTTP | Title | Message | +|---|---:|---:|---|---| +| `AFTER_FLIGHT_INVALID_ID` | `4223701` | 422 | Validation error | flight_inspection_id is invalid | +| `AFTER_FLIGHT_FLIGHT_INSPECTION_NOT_FOUND` | `4043702` | 404 | Not found | flight inspection not found | +| `AFTER_FLIGHT_INSPECTION_NOT_FOUND` | `4043703` | 404 | Not found | after flight inspection not found | +| `AFTER_FLIGHT_CAPABILITIES_UNAVAILABLE` | `4223704` | 422 | Validation error | helicopter capabilities could not be resolved | +| `AFTER_FLIGHT_NR1_REQUIRED` | `4223705` | 422 | Validation error | oil_engine_nr1_checked is required for this helicopter | +| `AFTER_FLIGHT_NR1_NOT_AVAILABLE` | `4223706` | 422 | Validation error | NR1 capability not available on this helicopter | +| `AFTER_FLIGHT_NR2_REQUIRED` | `4223707` | 422 | Validation error | oil_engine_nr2_checked is required for this helicopter | +| `AFTER_FLIGHT_NR2_NOT_AVAILABLE` | `4223708` | 422 | Validation error | NR2 capability not available on this helicopter | +| `AFTER_FLIGHT_LH_REQUIRED` | `4223709` | 422 | Validation error | hydraulic_lh_checked is required for this helicopter | +| `AFTER_FLIGHT_LH_NOT_AVAILABLE` | `4223710` | 422 | Validation error | LH capability not available on this helicopter | +| `AFTER_FLIGHT_RH_REQUIRED` | `4223711` | 422 | Validation error | hydraulic_rh_checked is required for this helicopter | +| `AFTER_FLIGHT_RH_NOT_AVAILABLE` | `4223712` | 422 | Validation error | RH capability not available on this helicopter | +| `AFTER_FLIGHT_MGB_REQUIRED` | `4223713` | 422 | Validation error | oil_transmission_mgb_checked is required for this helicopter | +| `AFTER_FLIGHT_MGB_NOT_AVAILABLE` | `4223714` | 422 | Validation error | MGB capability not available on this helicopter | +| `AFTER_FLIGHT_IGB_REQUIRED` | `4223715` | 422 | Validation error | oil_transmission_igb_checked is required for this helicopter | +| `AFTER_FLIGHT_IGB_NOT_AVAILABLE` | `4223716` | 422 | Validation error | IGB capability not available on this helicopter | +| `AFTER_FLIGHT_TGB_REQUIRED` | `4223717` | 422 | Validation error | oil_transmission_tgb_checked is required for this helicopter | +| `AFTER_FLIGHT_TGB_NOT_AVAILABLE` | `4223718` | 422 | Validation error | TGB capability not available on this helicopter | +| `AFTER_FLIGHT_INVALID_JSON` | `4003719` | 400 | Bad Request | invalid json payload | + +## Notes + +- `title` dan `detail` dapat berubah mengikuti copywriting; jangan dijadikan kontrak parsing. +- `code` dan `error_code` harus dianggap stabil sebagai kontrak integrasi BE↔FE. +- Uniqueness `code` dan `error_code` divalidasi lewat test registry di package [apperrorsx](../internal/shared/pkg/apperrorsx) (`registry_test.go`). +- Sumber definisi tunggal: [codes_defs.go](../internal/shared/pkg/apperrorsx/codes_defs.go) (konstanta `Code*`, `Case*`, dan variable `Err*` + `Registry`). +- Jika menambah error baru: daftarkan di `codes_defs.go` (konstanta + variable `Err*`) lalu masukkan ke `Registry`, kemudian update tabel di catalog ini. diff --git a/docs/file-lifecycle-s3-cleanup.md b/docs/file-lifecycle-s3-cleanup.md new file mode 100644 index 0000000..881c3d7 --- /dev/null +++ b/docs/file-lifecycle-s3-cleanup.md @@ -0,0 +1,124 @@ +# File Lifecycle and S3 Cleanup + +## Lifecycle States (DB source of truth) + +`file_files.lifecycle_status`: + +- `temp`: uploaded but not attached +- `active`: referenced by one or more business entities +- `orphan_pending`: currently unreferenced, waiting grace period +- `deleted`: backend cleanup has deleted object(s) and marked terminal state + +Supporting timestamps: + +- `created_at`: existing file creation time +- `attached_at`: set when file becomes active +- `orphaned_at`: set when file becomes unreferenced +- `lifecycle_deleted_at`: set when cleanup marks lifecycle `deleted` + +Notes: + +- Existing `status` column (`uploaded/processing/validated/ready/trashed`) remains for processing/trash flow. +- `deleted_at` is still used by trash semantics. Cleanup lifecycle uses `lifecycle_deleted_at`. + +## S3 Tags + +Single tag key: `gc` + +- `gc=temp`: temporary uploads +- `gc=keep`: active or protected files +- `gc=delete`: optional marker immediately before backend delete + +S3 tags are markers only. DB lifecycle state and DB reference checks are authoritative. + +## Cleanup Job + +Scheduler: API process loop (`startFileManagerLifecycleCleanupLoop`). + +Behavior: + +1. Query DB for `orphan_pending` files where `orphaned_at <= now - ORPHAN_GRACE_PERIOD_DAYS`. +2. Process in batches (`FILE_CLEANUP_BATCH_SIZE`). +3. Re-check DB references per file before deletion. +4. If referenced again: mark `active`, set/keep `gc=keep`. +5. If still unreferenced: + - optional `gc=delete` tag + - delete original and thumbnail S3 objects + - mark lifecycle `deleted` and set `lifecycle_deleted_at` +6. Per-file error handling; one failure does not stop the batch. + +The job is idempotent and retry-safe. + +## Environment Variables + +- `ENABLE_S3_TAGGING` (default `true`) +- `ENABLE_FILE_CLEANUP_JOB` (default `false`) +- `TEMP_FILE_TTL_DAYS` (default `7`) +- `ORPHAN_GRACE_PERIOD_DAYS` (default `30`) +- `FILE_CLEANUP_BATCH_SIZE` (default `100`) +- `FILE_CLEANUP_DRY_RUN` (default `true`) + +## Backfill (Dry-Run First) + +Command: + +```bash +go run ./cmd/file-lifecycle-backfill +``` + +Optional: + +- `BACKFILL_DRY_RUN=true|false` (default `true`) + +Output report includes: + +- active files +- probable orphan files +- missing S3 key +- invalid references +- skipped records + +## Required IAM Permissions + +Minimum required: + +- `s3:PutObjectTagging` +- `s3:GetObjectTagging` +- `s3:DeleteObject` +- `s3:GetObject` +- `s3:HeadObject` + +Only if infra automation updates lifecycle rules: + +- `s3:PutLifecycleConfiguration` + +## S3 Lifecycle Rules (Manual if no IaC) + +Because this repository does not define Terraform/CDK/CloudFormation for S3 bucket lifecycle, configure lifecycle rules in your infra/deployment stack: + +1. Rule `gc=temp`: + - Filter: object tag `gc=temp` + - Expire after `TEMP_FILE_TTL_DAYS` (recommended default 7 days) + +2. Optional rule `gc=delete`: + - Filter: object tag `gc=delete` + - Expire after 1 day + +Do not use lifecycle expiration as the orphan grace mechanism. Grace period is enforced by DB `orphaned_at`. + +## Rollout Plan + +1. Deploy schema + code with defaults (`cleanup job off`, `dry-run on`). +2. Enable tagging for new uploads (`ENABLE_S3_TAGGING=true`). +3. Validate attach/detach transitions in logs. +4. Run backfill dry-run and review report. +5. Enable cleanup job in dry-run mode. +6. Switch cleanup dry-run off after validation. +7. Apply S3 lifecycle rules for `gc=temp` (and optional `gc=delete`). + +## Rollback Plan + +1. Set `ENABLE_FILE_CLEANUP_JOB=false`. +2. Set `FILE_CLEANUP_DRY_RUN=true`. +3. Optionally set `ENABLE_S3_TAGGING=false`. +4. Pause/remove lifecycle rule `gc=delete` if enabled. diff --git a/docs/file-manager-aircraft-upload-create.md b/docs/file-manager-aircraft-upload-create.md new file mode 100644 index 0000000..84efbf7 --- /dev/null +++ b/docs/file-manager-aircraft-upload-create.md @@ -0,0 +1,143 @@ +# File Manager Aircraft Image Flow + +This document explains the upload + create flow specifically for aircraft images. + +## Purpose + +This flow is used to upload aircraft images (drag-and-drop) and persist file metadata into a forced folder hierarchy based on the aircraft designation. + +Main differences compared to the regular file flow: +- dedicated upload endpoint: `POST /api/v1/file-manager/files/upload-aircraft-image` +- dedicated create endpoint: `POST /api/v1/file-manager/files/create-aircraft-image` +- `content_type` must be `image/*` +- target folder is forced to: `__system_root_files__ > aircraft > ` +- `aircraft_uuid` is required in the create request per item + +## Prerequisites + +1. User is logged in and has `file_manager.create` permission. +2. Frontend has per-file data: `name`, `content_type`, `size_bytes`, and binary file. + +## Step 1 - Request Upload Intent (Aircraft) + +Endpoint: +- `POST /api/v1/file-manager/files/upload-aircraft-image` + +Request body (bulk, minimum 1 item): + +```json +{ + "data": [ + { + "type": "file_manager_file_upload_intent", + "attributes": { + "name": "aircraft-front.jpg", + "content_type": "image/jpeg", + "size_bytes": 42170 + } + } + ] +} +``` + +Important validation rules in this endpoint: +- `data` must be an array +- `type` must be `file_manager_file_upload_intent` +- `name` is required +- `content_type` is required and must start with `image/` +- `size_bytes >= 0` + +Per-item response (partial success is possible), FE receives: +- `upload_intent_uuid` +- `upload_url` +- `method` (`PUT`) +- `required_headers` +- object info (`bucket`, `key`) +- `expires_at`, `expires_in_seconds` + +## Step 2 - Upload Binary Directly to Object Storage + +For each item with `success: true` from step 1: +1. take `upload_url` +2. perform `PUT` binary file to that URL +3. send headers according to `required_headers` + +Example: + +```javascript +await fetch(upload_url, { + method: "PUT", + headers: { + "Content-Type": file.type + }, + body: file +}); +``` + +Notes: +- 1 file = 1 `PUT` request +- parallel upload is allowed with a concurrency limit +- files that fail `PUT` must not be sent to the create step + +## Step 3 - Create File Record (Aircraft) + +Endpoint: +- `POST /api/v1/file-manager/files/create-aircraft-image` + +Request body: + +```json +{ + "data": [ + { + "type": "file_manager_file_create", + "attributes": { + "upload_intent_uuid": "019d8215-c622-7576-86bd-3ddeecaf4867", + "aircraft_uuid": "019d8215-c799-7209-9237-2e2f5d0f8709", + "name": "aircraft-front.jpg" + } + } + ] +} +``` + +Important validation rules: +- `data` must be an array +- `type` must be `file_manager_file_create` or `file_manager_file` +- `upload_intent_uuid` is required and must be a valid UUID +- `aircraft_uuid` is required and must be a valid UUID +- `name` is required + +Aircraft-specific behavior: +- backend loads aircraft by `aircraft_uuid` and reads its `designation` +- backend resolves/creates folder hierarchy: + - `__system_root_files__` (system root) + - `aircraft` under system root + - `` under `aircraft` (example: `OE-XHZ`) +- final file is created inside `` folder +- `folder_id` from payload is ignored in this endpoint because folder is derived from aircraft designation + +## Step 4 - Backend Process During Create + +For each valid item: +1. backend validates and loads aircraft from `aircraft_uuid` +2. backend resolves/creates `__system_root_files__ > aircraft > ` +3. backend validates uploaded object from `upload_intent_uuid` +4. backend creates file node in `` folder +5. file status enters normal file manager lifecycle (`uploaded -> processing -> validated -> ready` or failed) + +## Status Codes + +- `201`: all create items succeed +- `207`: partial success (mixed success and failure) +- `404`: aircraft not found (per-item in bulk response) +- `422`: payload validation error +- `400`: invalid JSON payload +- `503`: aircraft service unavailable + +## FE Integration Summary + +1. call `upload-aircraft-image` +2. upload binary to storage via presigned URL +3. call `create-aircraft-image` only for items with successful `PUT` +4. store resulting file `id` for reference in aircraft module diff --git a/docs/file-manager-attachment.md b/docs/file-manager-attachment.md new file mode 100644 index 0000000..43560f2 --- /dev/null +++ b/docs/file-manager-attachment.md @@ -0,0 +1,73 @@ +# File Manager + +# Attachment (Step by Step) + +## A. Tambah Attachment + +### Endpoint +- `POST /api/v1/file-manager/attachments/create` + +### FE kirim request ini + +```json +{ + "data": { + "type": "file_manager_attachment_create", + "attributes": { + "file_id": "file-uuid", + "ref_type": "helicopter", + "ref_id": "business-entity-uuid", + "category": "document", + "sort_order": 0, + "is_primary": false + } + } +} +``` + +Keterangan: +- `file_id`, `ref_type`, `ref_id` wajib +- `category` opsional +- `sort_order` opsional (default 0) +- `is_primary` opsional (default false) + +## B. Lihat Attachment by Reference + +### Endpoint +- `GET /api/v1/file-manager/attachments/get-all?ref_type=&ref_id=` + +### Contoh +- `GET /api/v1/file-manager/attachments/get-all?ref_type=helicopter&ref_id=business-entity-uuid` + +Catatan urutan default: +- jika query `sort` tidak dikirim, urutan default adalah `sort_order ASC, created_at DESC` +- artinya untuk `sort_order` yang sama, attachment terbaru tampil lebih dulu + +## C. Detail Attachment + +### Endpoint +- `GET /api/v1/file-manager/attachments/get/:uuid` + +## D. Hapus Attachment (Detach) + +### Endpoint +- `DELETE /api/v1/file-manager/attachments/delete/:uuid` + +### Contoh response + +```json +{ + "data": { + "type": "file_manager_attachment_delete", + "attributes": { + "detached": true + } + } +} +``` + +## Ringkasan +1. create relation file -> entity bisnis +2. list by `ref_type + ref_id` +3. get detail relation +4. delete relation (detach) diff --git a/docs/file-manager-base-upload-create.md b/docs/file-manager-base-upload-create.md new file mode 100644 index 0000000..47b8b52 --- /dev/null +++ b/docs/file-manager-base-upload-create.md @@ -0,0 +1,62 @@ +# File Manager Base Image Flow + +This document explains upload + create flow specifically for base images. + +## Purpose + +This flow uploads base images and persists file metadata into a forced folder hierarchy: + +- `__system_root_files__ > bases > > ` + +Main differences compared to regular file flow: + +- dedicated upload endpoint: `POST /api/v1/file-manager/files/upload-base-image` +- dedicated create endpoint: `POST /api/v1/file-manager/files/create-base-image` +- `content_type` must be `image/*` +- `base_uuid` is required in the create request per item +- create step performs hard replace of existing files in target folder before creating new file + +## Step 1 - Request Upload Intent (Base) + +Endpoint: + +- `POST /api/v1/file-manager/files/upload-base-image` + +Validation highlights: + +- `type` must be `file_manager_file_upload_intent` +- `name` required +- `content_type` required and must start with `image/` +- `size_bytes >= 0` + +## Step 2 - Upload Binary to Object Storage + +For each successful intent, frontend uploads binary with `PUT` to `upload_url`. + +## Step 3 - Create File Record (Base) + +Endpoint: + +- `POST /api/v1/file-manager/files/create-base-image` + +Validation highlights: + +- `type` must be `file_manager_file_create` or `file_manager_file` +- `upload_intent_uuid` required and valid UUID +- `base_uuid` required and valid UUID +- `name` required + +Backend behavior: + +1. load base from `base_uuid` +2. resolve `base-type` from base category (e.g. `hems` or `regular`) +3. resolve/create folder path `__system_root_files__ > bases > > ` +4. hard replace: delete + purge all existing files in that target folder +5. create file node from upload intent + +Hard replace cleanup target: + +- file row in DB +- original object in S3 +- thumbnail object in S3 (if exists) +- related attachment metadata diff --git a/docs/file-manager-collabora-wopi-flow.md b/docs/file-manager-collabora-wopi-flow.md new file mode 100644 index 0000000..2bfdac2 --- /dev/null +++ b/docs/file-manager-collabora-wopi-flow.md @@ -0,0 +1,829 @@ +# File Manager Collabora / WOPI Flow + +Dokumen ini menjelaskan alur frontend untuk membuka editor Collabora lewat backend WOPI. + +Prinsip utama: + +1. Frontend hanya memanggil App API. +2. Frontend tidak pernah memanggil endpoint `/wopi/...` secara langsung. +3. Backend yang mengurus token, proof, S3, lock, autosave, dan finalisasi. + +Tambahan penting untuk flow helicopter template: + +1. hasil `create-from-template` akan dibuat sebagai file manager file biasa, bukan template +2. backend juga akan memastikan file itu masuk ke `helicopter_files` section `fpwb` +3. kalau frontend membuka template yang sama untuk `takeover_id` yang sama, backend akan reuse file existing bila sudah ada +4. saat child `fpwb` baru berhasil dibuat, backend akan kirim SSE agar frontend bisa langsung append item baru ke list +5. kalau file hasil template punya `takeover_id`, endpoint `GET /api/v1/helicopter-files/get/{helicopter_id}` dan `GET /api/v1/helicopter-files/list` akan menyembunyikan item itu dari user yang tidak punya akses takeover tersebut + +## Endpoint Yang Dipakai Frontend + +### 1) Buat file baru dari template + +`POST /api/v1/file-manager/files/create-from-template` + +Request: + +```json +{ + "data": { + "type": "file_manager_file_create_from_template", + "attributes": { + "template_uuid": "019f0386-0a00-7a4a-a2d8-30afc0e0c3ad", + "helicopter_id": "019e8713-aa19-7fb7-a510-a0332dc69697", + "takeover_id": "019f0386-0a00-7a4a-a2d8-30afc0e0c3af", + "name": "collabora-draft.docx" + } + } +} +``` + +Response: + +```json +{ + "data": { + "file_id": "019f0a12-1111-7abc-8def-222222222222", + "collabora_action": "https://collabora.example.com/.../WOPISrc=...", + "access_token": "eyJhbGciOiJIUzI1NiIs...", + "access_token_ttl": 1800000 + } +} +``` + +### 2) Buka editor untuk file existing + +`GET /api/v1/file-manager/files/{fileId}/edit-session` + +Response shape sama: + +```json +{ + "data": { + "file_id": "019f0a12-1111-7abc-8def-222222222222", + "collabora_action": "https://collabora.example.com/.../WOPISrc=...", + "access_token": "eyJhbGciOiJIUzI1NiIs...", + "access_token_ttl": 1800000 + } +} +``` + +### 3) Ambil daftar helicopter files + +`GET /api/v1/helicopter-files/get/{helicopter_id}` + +Endpoint ini yang dipakai frontend untuk melihat child file yang muncul di helicopter file collection. + +Untuk hasil `create-from-template`, item baru akan muncul di: + +1. `data.attributes.files.prepare` +2. section backend = `fpwb` +3. secara UI biasanya ditampilkan sebagai area Flight Preparation and WB + +### 4) Dengarkan event child baru + +`GET /api/v1/helicopter-files/events?helicopter_id={helicopter_id}` + +Stream ini dipakai supaya frontend bisa langsung append child baru tanpa reload penuh. + +Event name: + +`helicopter.file.created` + +### SSE dapet dari mana? + +SSE ini bukan datang dari Collabora, bukan dari browser local state, dan bukan dari endpoint `/wopi/...`. + +Sumbernya adalah backend App API: + +1. frontend subscribe ke `GET /api/v1/helicopter-files/events?helicopter_id={helicopter_id}` +2. route ini masuk ke handler SSE backend +3. backend hanya akan mengirim event ke user yang sedang subscribe +4. event difilter lagi dengan `helicopter_id` query param + +Kapan event dipublish: + +1. frontend memanggil `POST /api/v1/file-manager/files/create-from-template` +2. backend membuat atau reuse file manager file +3. backend memastikan ada row `helicopter_files` di section `fpwb` +4. kalau row `helicopter_files` itu baru dibuat saat request ini, backend publish event `helicopter.file.created` +5. stream `/api/v1/helicopter-files/events` mengirim event itu ke frontend yang subscribe + +Artinya: + +1. kalau child `fpwb` sudah ada dari sebelumnya, backend tidak akan publish event create lagi +2. kalau frontend telat subscribe, fallback paling aman tetap re-fetch `GET /api/v1/helicopter-files/get/{helicopter_id}` +3. SSE ini hanya untuk sinkronisasi UI realtime, bukan source of truth utama + +## Flow Create From Template + +### Step 1 - Frontend minta editor session + +Frontend mengirim `template_uuid`, `helicopter_id`, `name`, dan boleh menambahkan `takeover_id`. +`template_uuid` harus mengarah ke `helicopter_file.helicopter_file_id` dari helicopter yang sama dengan `helicopter_id`, bukan ke file takeover biasa. + +Backend akan menjalankan alur seperti ini: + +```text +create-from-template + -> cek user session + -> cek template memang milik helicopter itu + -> jika ada takeover_id: + -> cek apakah kombinasi template + takeover sudah punya file existing + -> jika sudah ada: + -> reuse file existing + -> pastikan metadata template + takeover tersimpan + -> pastikan ada child helicopter_files di section fpwb + -> jika child baru dibuat, publish SSE helicopter.file.created + -> mint WOPI token + -> balikin Collabora action URL + -> jika belum ada: + -> buat draft file baru dari template + -> copy object template ke key final file baru + -> simpan metadata template + takeover + -> pastikan ada child helicopter_files di section fpwb + -> jika child baru dibuat, publish SSE helicopter.file.created + -> mint WOPI token + -> balikin Collabora action URL + -> jika takeover_id tidak ada: + -> buat draft file baru dari template + -> copy object template ke key final file baru + -> simpan metadata template + -> pastikan ada child helicopter_files di section fpwb + -> jika child baru dibuat, publish SSE helicopter.file.created + -> mint WOPI token + -> balikin Collabora action URL +``` + +Catatan untuk step 9: + +1. SSE dipublish oleh backend App API pada request `create-from-template` +2. jadi FE yang sedang membuka halaman helicopter files bisa langsung menerima event tanpa menunggu save dari Collabora +3. source of truth event tetap berasal dari proses create child row di backend + +### Step 2 - Frontend simpan response + +Frontend wajib menyimpan: + +1. `file_id` +2. `collabora_action` +3. `access_token` +4. `access_token_ttl` dalam milidetik + +`file_id` dipakai sebagai join key untuk session berikutnya. +`file_id` ini juga bisa dikirim lagi saat frontend create takeover lewat field `edited_template_file_ids`. + +Kalau tujuan frontend adalah menampilkan child di helicopter files: + +1. `file_id` adalah ID file manager file +2. `helicopter_file_id` tidak ada di response create-from-template +3. `helicopter_file_id` akan muncul lewat: + `GET /api/v1/helicopter-files/get/{helicopter_id}` + atau SSE `helicopter.file.created` + +### Step 3 - Frontend buka Collabora + +Frontend tidak boleh hanya redirect atau `window.open(collabora_action)` begitu saja. + +`collabora_action` harus dibuka dengan `POST` form yang ikut mengirim: + +1. `access_token` +2. `access_token_ttl` dalam milidetik + +Ini penting karena backend WOPI membaca token dari query `access_token` atau header bearer pada request WOPI yang datang dari Collabora. +Kalau frontend hanya membuka `collabora_action` tanpa token bootstrap, backend akan mengembalikan error seperti: + +1. `401 FILE_MANAGER_WOPI_INVALID_TOKEN` +2. `token is malformed: token contains an invalid number of segments` + +Frontend membuka editor memakai: + +1. `collabora_action` sebagai `form action` +2. `access_token` sebagai hidden input +3. `access_token_ttl` sebagai hidden input + +Rule praktis: + +1. jangan pernah memanggil `/wopi/...` dari browser +2. jangan pernah expose S3 URL atau credential ke frontend +3. gunakan `collabora_action` sebagai base URL editor +4. jangan buka `collabora_action` tanpa submit `access_token` + +## File Akan Muncul Di Mana + +Setelah `create-from-template` sukses, hasil akhirnya ada di dua tempat berbeda yang perlu dibedakan: + +### 1) File manager file + +Ini adalah object file utama yang dibuka oleh Collabora. + +Identifier utamanya: + +1. `file_id` + +Endpoint yang relevan: + +1. `GET /api/v1/file-manager/files/{fileId}/edit-session` +2. WOPI internal backend `/wopi/files/{fileId}` dan `/wopi/files/{fileId}/contents` + +### 2) Helicopter file child + +Ini adalah row bisnis yang membuat file tadi muncul di halaman helicopter files. + +Lokasinya: + +1. `GET /api/v1/helicopter-files/get/{helicopter_id}` +2. response path `data.attributes.files.prepare` +3. section row = `fpwb` + +Field penting item collection: + +1. `helicopter_file_id` +2. `attachment.id` +3. `attachment.file_name` + +Catatan: + +1. child ini bukan parent folder file manager +2. child ini adalah row di tabel `helicopter_files` +3. relasinya menunjuk ke file hasil template lewat `source_file_id` + +## Contoh Integrasi Next.js + +Bagian ini menunjukkan contoh yang lengkap untuk Next.js App Router. + +Asumsi: + +1. user sudah login ke App API +2. frontend memanggil backend App API, bukan endpoint `/wopi/...` +3. backend mengembalikan `file_id`, `collabora_action`, `access_token`, dan `access_token_ttl` + +### Tipe response + +```ts +export type CollaboraSessionResponse = { + data: { + file_id: string + collabora_action: string + access_token: string + access_token_ttl: number + } +} +``` + +### Server action atau API helper + +Contoh helper untuk memanggil backend `create-from-template`. + +```ts +// app/lib/file-manager.ts +export type CreateFromTemplateInput = { + templateUuid: string + helicopterId: string + takeoverId?: string + name: string +} + +export type CollaboraSessionResponse = { + data: { + file_id: string + collabora_action: string + access_token: string + access_token_ttl: number + } +} + +export async function createFromTemplate( + input: CreateFromTemplateInput, + accessToken: string, +): Promise { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/v1/file-manager/files/create-from-template`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + data: { + type: "file_manager_file_create_from_template", + attributes: { + template_uuid: input.templateUuid, + helicopter_id: input.helicopterId, + takeover_id: input.takeoverId, + name: input.name, + }, + }, + }), + cache: "no-store", + }, + ) + + if (!response.ok) { + throw new Error(`Create from template failed with status ${response.status}`) + } + + return response.json() +} +``` + +Contoh helper untuk membuka file existing: + +```ts +// app/lib/file-manager.ts +export async function getEditSession( + fileId: string, + accessToken: string, +): Promise { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/v1/file-manager/files/${fileId}/edit-session`, + { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + }, + cache: "no-store", + }, + ) + + if (!response.ok) { + throw new Error(`Edit session failed with status ${response.status}`) + } + + return response.json() +} +``` + +### Komponen client untuk bootstrap Collabora + +Komponen ini membuat form hidden dan auto-submit ke `collabora_action`. + +```tsx +// app/components/collabora-launcher.tsx +"use client" + +import { useEffect, useRef } from "react" + +type Props = { + collaboraAction: string + accessToken: string + accessTokenTTL: number // milliseconds + target?: string +} + +export function CollaboraLauncher({ + collaboraAction, + accessToken, + accessTokenTTL, + target = "collabora-editor", +}: Props) { + const formRef = useRef(null) + + useEffect(() => { + formRef.current?.submit() + }, []) + + return ( + <> +
+ + +
+ +