Merge branch 'main' of ssh://git.trai-infra.comstack.de:9922/bob.molitor/frontend
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
name: Build and Push Docker Image only on Conventional Commits
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_REGISTRY: git.trai-infra.comstack.de
|
||||||
|
DOCKER_IMAGE: marian.lippitz/tap-frontend
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Validate Conventional Commit
|
||||||
|
id: validate
|
||||||
|
run: |
|
||||||
|
if git log -1 --pretty=%B | grep -Eq "^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert)(\(.+\))?: .+"; then
|
||||||
|
echo "valid=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "valid=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Stop if not a Conventional Commit
|
||||||
|
if: ${{ steps.validate.outputs.valid == 'false' }}
|
||||||
|
run: |
|
||||||
|
echo "Latest commit is not a valid Conventional Commit. Skipping build."
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
- name: Docker Login
|
||||||
|
env:
|
||||||
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "$DOCKER_PASSWORD" | docker login $DOCKER_REGISTRY -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
|
||||||
|
- name: Build Docker Image
|
||||||
|
run: |
|
||||||
|
docker build -t "$DOCKER_REGISTRY/$DOCKER_IMAGE:latest" .
|
||||||
|
|
||||||
|
- name: Push Docker Image
|
||||||
|
run: |
|
||||||
|
docker push "$DOCKER_REGISTRY/$DOCKER_IMAGE:latest"
|
||||||
|
|
||||||
|
- name: Trigger Kubernetes Redeployment
|
||||||
|
uses: alpine/kubectl:1.34.1
|
||||||
|
env:
|
||||||
|
KUBECONFIG: ${{ secrets.KUBECONFIG }}
|
||||||
|
with:
|
||||||
|
args: >
|
||||||
|
patch deployment tap-frontend -n frontend
|
||||||
|
-p '{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"'"$(date -u +'%Y-%m-%dT%H:%M:%SZ')"'"}}}}}'
|
||||||
|
&& kubectl rollout status deployment tap-frontend -n frontend --timeout=120s
|
||||||
+10
-2
@@ -24,9 +24,11 @@
|
|||||||
# Provide php alias
|
# Provide php alias
|
||||||
RUN ln -sf /usr/bin/php82 /usr/bin/php
|
RUN ln -sf /usr/bin/php82 /usr/bin/php
|
||||||
|
|
||||||
|
# Add custom PHP logging configuration
|
||||||
|
COPY deploy/php-fpm/zz-logging.ini /etc/php82/conf.d/
|
||||||
|
|
||||||
# Create user, dirs, and sockets
|
# Create user, dirs, and sockets
|
||||||
RUN addgroup -S web && adduser -S web -G web \
|
RUN mkdir -p /run/nginx /run/php /var/www/html /var/log/supervisor
|
||||||
&& mkdir -p /run/nginx /run/php /var/www/html /var/log/supervisor
|
|
||||||
|
|
||||||
# Configure PHP-FPM socket
|
# Configure PHP-FPM socket
|
||||||
RUN sed -i 's|^listen = 127\\.0\\.0\\.1:9000|listen = /run/php/php-fpm.sock|' /etc/php82/php-fpm.d/www.conf \
|
RUN sed -i 's|^listen = 127\\.0\\.0\\.1:9000|listen = /run/php/php-fpm.sock|' /etc/php82/php-fpm.d/www.conf \
|
||||||
@@ -53,6 +55,12 @@
|
|||||||
&& chown -R nginx:nginx storage bootstrap/cache \
|
&& chown -R nginx:nginx storage bootstrap/cache \
|
||||||
&& chmod -R ug+rwX storage bootstrap/cache
|
&& chmod -R ug+rwX storage bootstrap/cache
|
||||||
|
|
||||||
|
# Create Nginx error log file with correct permissions
|
||||||
|
RUN mkdir -p /var/log/nginx \
|
||||||
|
&& touch /var/log/nginx/error.log \
|
||||||
|
&& chown -R nginx:nginx /var/log/nginx \
|
||||||
|
&& chown -R nginx:nginx /var/log/php82 \
|
||||||
|
&& chmod -R 664 /var/log/nginx/error.log
|
||||||
# Build frontend assets
|
# Build frontend assets
|
||||||
RUN npm ci && npm run build
|
RUN npm ci && npm run build
|
||||||
|
|
||||||
|
|||||||
+77
-16
@@ -1,27 +1,88 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
# Docker entrypoint for Nginx + PHP/Laravel
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
SERVER_NAME="${SERVER_NAME:-$(hostname -f 2>/dev/null || hostname)}"
|
# -------- defaults (override via env) --------
|
||||||
|
: "${APP_DIR:=/var/www/html}"
|
||||||
|
: "${NGINX_TEMPLATE:=/etc/nginx/templates/default.conf.template}"
|
||||||
|
: "${NGINX_HTTPD_DIR:=/etc/nginx/http.d}"
|
||||||
|
: "${NGINX_CONF_OUT:=${NGINX_HTTPD_DIR}/default.conf}"
|
||||||
|
: "${HTPASSWD_PATH:=/etc/nginx/htpasswd}"
|
||||||
|
: "${BASIC_AUTH_REALM:=Restricted Content}"
|
||||||
|
: "${SUPERVISOR_CONF:=/etc/supervisor/supervisord.conf}"
|
||||||
|
|
||||||
|
log() { printf '[entrypoint] %s\n' "$*"; }
|
||||||
|
err() { printf '[entrypoint][ERROR] %s\n' "$*" >&2; }
|
||||||
|
die() { err "$*"; exit 1; }
|
||||||
|
need() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"; }
|
||||||
|
|
||||||
|
# required tools
|
||||||
|
need envsubst; need nginx; need php; need supervisord
|
||||||
|
|
||||||
|
# server name
|
||||||
|
SERVER_NAME="${SERVER_NAME:-$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo localhost)}"
|
||||||
export SERVER_NAME
|
export SERVER_NAME
|
||||||
|
|
||||||
# Ensure dirs
|
# truthy helper
|
||||||
mkdir -p /etc/nginx/http.d /etc/nginx/conf.d /run/nginx /run/php
|
is_truthy() {
|
||||||
|
case "${1:-}" in 1|true|TRUE|yes|YES|on|ON) return 0;; *) return 1;; esac
|
||||||
|
}
|
||||||
|
|
||||||
# Render vhost ONLY into http.d so it's inside http{} context
|
# -------- basic auth directives --------
|
||||||
if [ -f /etc/nginx/templates/default.conf.template ]; then
|
BASIC_AUTH_DIRECTIVES=""
|
||||||
envsubst '$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/http.d/default.conf
|
if is_truthy "${ENABLE_BASIC_AUTH:-}"; then
|
||||||
|
log "Enabling basic authentication"
|
||||||
|
[ -f "$HTPASSWD_PATH" ] || die "htpasswd not found: $HTPASSWD_PATH"
|
||||||
|
BASIC_AUTH_DIRECTIVES=$(cat <<'EOF'
|
||||||
|
auth_basic "__REALM__";
|
||||||
|
auth_basic_user_file __HTPASSWD__;
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
# safe placeholder substitution
|
||||||
|
esc() { printf '%s' "$1" | sed 's/[&/\]/\\&/g'; }
|
||||||
|
BASIC_AUTH_DIRECTIVES=$(printf '%s' "$BASIC_AUTH_DIRECTIVES" \
|
||||||
|
| sed "s|__REALM__|$(esc "$BASIC_AUTH_REALM")|g" \
|
||||||
|
| sed "s|__HTPASSWD__|$(esc "$HTPASSWD_PATH")|g")
|
||||||
else
|
else
|
||||||
echo "[entrypoint] ERROR: missing /etc/nginx/templates/default.conf.template" >&2
|
log "Basic authentication is disabled"
|
||||||
exit 1
|
fi
|
||||||
|
export BASIC_AUTH_DIRECTIVES
|
||||||
|
|
||||||
|
# -------- render nginx config (atomic) --------
|
||||||
|
[ -f "$NGINX_TEMPLATE" ] || die "Missing template: $NGINX_TEMPLATE"
|
||||||
|
[ -d "$NGINX_HTTPD_DIR" ] || die "Missing nginx conf dir: $NGINX_HTTPD_DIR"
|
||||||
|
|
||||||
|
tmpconf="$(mktemp "${NGINX_CONF_OUT}.XXXXXX")"
|
||||||
|
envsubst '$SERVER_NAME,$BASIC_AUTH_DIRECTIVES' <"$NGINX_TEMPLATE" >"$tmpconf" \
|
||||||
|
|| die "envsubst failed"
|
||||||
|
|
||||||
|
mv -f "$tmpconf" "$NGINX_CONF_OUT"
|
||||||
|
log "Wrote nginx config -> $NGINX_CONF_OUT"
|
||||||
|
|
||||||
|
# final sanity test against active config tree
|
||||||
|
nginx -t || die "nginx config test failed after install"
|
||||||
|
|
||||||
|
# -------- Laravel cache clears (best-effort) --------
|
||||||
|
if [ -f "$APP_DIR/artisan" ]; then
|
||||||
|
log "Clearing Laravel caches"
|
||||||
|
( cd "$APP_DIR" && php artisan config:clear || log "config:clear failed (continuing)" )
|
||||||
|
( cd "$APP_DIR" && php artisan cache:clear || log "cache:clear failed (continuing)" )
|
||||||
|
else
|
||||||
|
log "No Laravel app at $APP_DIR; skipping artisan"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[entrypoint] Using SERVER_NAME=$SERVER_NAME"
|
# -------- entrypoint behavior --------
|
||||||
nginx -t || { echo "[entrypoint] nginx config test failed" >&2; cat /var/log/nginx/error.log || true; exit 1; }
|
# If the first arg is an option (starts with -), assume supervisord.
|
||||||
|
if [ "${1:-}" ] && [ "${1#-}" != "$1" ]; then
|
||||||
|
set -- /usr/bin/supervisord -n -c "$SUPERVISOR_CONF" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
php artisan config:clear
|
# If they provided a command (e.g., sh, bash, php -v), run it.
|
||||||
php artisan cache:clear
|
if [ "${1:-}" ] && [ "$1" != "/usr/bin/supervisord" ] && [ "$1" != "supervisord" ]; then
|
||||||
|
log "Executing custom command: $*"
|
||||||
envsubst '$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/http.d/default.conf
|
exec "$@"
|
||||||
|
fi
|
||||||
exec /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf
|
|
||||||
|
|
||||||
|
# Default: start supervisord in the foreground (PID 1)
|
||||||
|
log "Starting supervisord"
|
||||||
|
exec /usr/bin/supervisord -n -c "$SUPERVISOR_CONF"
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
user nginx;
|
user nginx;
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
pid /run/nginx.pid;
|
# Send logs to container fds (works regardless of privileges)
|
||||||
|
error_log /proc/self/fd/2 warn;
|
||||||
|
pid /run/nginx/nginx.pid;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
worker_connections 1024;
|
worker_connections 1024;
|
||||||
@@ -15,14 +17,13 @@ http {
|
|||||||
'$status $body_bytes_sent "$http_referer" '
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
access_log /dev/stdout main;
|
access_log /proc/self/fd/1 main;
|
||||||
error_log /dev/stderr warn;
|
# (No extra error_log here; the top-level one above is enough)
|
||||||
|
|
||||||
sendfile on;
|
sendfile on;
|
||||||
keepalive_timeout 65;
|
keepalive_timeout 65;
|
||||||
|
|
||||||
# Server Blöcke sind NICHT hier!
|
# Include vhosts (your Laravel server block lives in one of these)
|
||||||
# Stattdessen in:
|
|
||||||
include /etc/nginx/conf.d/*.conf;
|
include /etc/nginx/conf.d/*.conf;
|
||||||
include /etc/nginx/http.d/*.conf;
|
include /etc/nginx/http.d/*.conf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,42 +5,89 @@ server {
|
|||||||
root /var/www/html/public;
|
root /var/www/html/public;
|
||||||
index index.php;
|
index index.php;
|
||||||
|
|
||||||
# Cache-Control für statische Dateien
|
# -------------------------------------------------
|
||||||
location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff2?|eot|ttf|otf|webp|avif)$ {
|
# Redirect all HTTP requests to HTTPS (optional, remove if TLS is handled upstream)
|
||||||
|
# -------------------------------------------------
|
||||||
|
# return 301 https://$host$request_uri;
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
# Livewire + Laravel routes (must come before static block)
|
||||||
|
# -------------------------------------------------
|
||||||
|
location ^~ /livewire/ {
|
||||||
|
try_files $uri /index.php?$query_string;
|
||||||
|
expires off;
|
||||||
|
add_header Cache-Control "no-store";
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
# Static assets with long caching
|
||||||
|
# -------------------------------------------------
|
||||||
|
location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff2?|eot|ttf|otf|webp|avif)(\?.*)?$ {
|
||||||
expires 7d;
|
expires 7d;
|
||||||
add_header Cache-Control "public, max-age=604800, immutable";
|
add_header Cache-Control "public, max-age=604800, immutable";
|
||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Haupt-Entry-Point (Laravel)
|
# -------------------------------------------------
|
||||||
|
# Main Laravel entry point
|
||||||
|
# -------------------------------------------------
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.php?$query_string;
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
${BASIC_AUTH_DIRECTIVES}
|
||||||
}
|
}
|
||||||
|
|
||||||
# PHP-FPM Verarbeitung
|
# -------------------------------------------------
|
||||||
|
# PHP-FPM configuration (matches your Alpine socket setup)
|
||||||
|
# -------------------------------------------------
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
fastcgi_pass 127.0.0.1:9000; # <-- switch from socket to TCP
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
fastcgi_param DOCUMENT_ROOT $document_root;
|
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||||
|
|
||||||
fastcgi_read_timeout 60s;
|
fastcgi_read_timeout 60s;
|
||||||
fastcgi_buffering on;
|
fastcgi_buffering on;
|
||||||
fastcgi_buffers 16 16k;
|
fastcgi_buffers 16 16k;
|
||||||
fastcgi_buffer_size 32k;
|
fastcgi_buffer_size 32k;
|
||||||
|
|
||||||
# Forward the X-Forwarded-Proto header
|
# --- Proxy headers for HTTPS and load balancers ---
|
||||||
fastcgi_param HTTPS $http_x_forwarded_proto;
|
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_HOST $http_host;
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
# --- Tell PHP it's HTTPS when forwarded ---
|
||||||
|
set $https_flag "";
|
||||||
|
if ($http_x_forwarded_proto = "https") { set $https_flag "on"; }
|
||||||
|
fastcgi_param HTTPS $https_flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Maximale Client Upload-Größe
|
# -------------------------------------------------
|
||||||
|
# Upload limits
|
||||||
|
# -------------------------------------------------
|
||||||
client_max_body_size 64m;
|
client_max_body_size 64m;
|
||||||
|
|
||||||
# Sicherheits-Header
|
# -------------------------------------------------
|
||||||
|
# Security headers
|
||||||
|
# -------------------------------------------------
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
||||||
|
|
||||||
# Gzip-Kompression
|
# -------------------------------------------------
|
||||||
|
# Gzip compression
|
||||||
|
# -------------------------------------------------
|
||||||
|
gzip on;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
application/json
|
||||||
|
application/javascript
|
||||||
|
text/xml
|
||||||
|
application/xml
|
||||||
|
application/xml+rss
|
||||||
|
image/svg+xml;
|
||||||
|
gzip_min_length 1000;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_vary on;
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
listen = 127.0.0.1:9000
|
|
||||||
user = nginx
|
|
||||||
group = nginx
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
PORT="${APP_PORT:-8080}"
|
|
||||||
|
|
||||||
# 1) App Health
|
|
||||||
curl -fsS "http://127.0.0.1:${PORT}/healthz" >/dev/null
|
|
||||||
|
|
||||||
# 2) PHP-FPM Ping via Nginx
|
|
||||||
curl -fsS "http://127.0.0.1:${PORT}/fpm-ping" >/dev/null
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# keine 'user' Direktive im non-root Betrieb
|
|
||||||
worker_processes auto;
|
|
||||||
error_log /dev/stderr warn;
|
|
||||||
pid /tmp/nginx.pid;
|
|
||||||
|
|
||||||
events { worker_connections 1024; }
|
|
||||||
|
|
||||||
http {
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
access_log /dev/stdout main;
|
|
||||||
|
|
||||||
sendfile on;
|
|
||||||
keepalive_timeout 65;
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
# Temp/Cache-Pfade, die 'nginx' gehören
|
|
||||||
client_body_temp_path /var/lib/nginx/tmp/client_body 1 2;
|
|
||||||
proxy_temp_path /var/lib/nginx/tmp/proxy 1 2;
|
|
||||||
fastcgi_temp_path /var/lib/nginx/tmp/fastcgi 1 2;
|
|
||||||
scgi_temp_path /var/lib/nginx/tmp/scgi 1 2;
|
|
||||||
uwsgi_temp_path /var/lib/nginx/tmp/uwsgi 1 2;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/*.conf;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 8080;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
root /var/www/html/public;
|
|
||||||
index index.php index.html;
|
|
||||||
|
|
||||||
# Health checks
|
|
||||||
location = /healthz {
|
|
||||||
access_log off;
|
|
||||||
return 200 "ok\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
# FPM ping/status (optional, secure in prod)
|
|
||||||
location = /fpm-ping {
|
|
||||||
access_log off;
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
|
||||||
fastcgi_param SCRIPT_NAME /ping;
|
|
||||||
fastcgi_param PATH_INFO /ping;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /fpm-status {
|
|
||||||
access_log off;
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
|
||||||
fastcgi_param SCRIPT_NAME /status;
|
|
||||||
fastcgi_param PATH_INFO /status;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main app
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.php?$query_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
# PHP handling
|
|
||||||
location ~ \.php$ {
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_index index.php;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
||||||
fastcgi_read_timeout 60s;
|
|
||||||
fastcgi_buffers 16 16k;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Deny hidden files
|
|
||||||
location ~ /\.ht {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Cache static assets
|
|
||||||
location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|ico|webp|woff2?)$ {
|
|
||||||
access_log off;
|
|
||||||
expires 7d;
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+940
@@ -0,0 +1,940 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: Trusted AI Analyst Data API
|
||||||
|
version: '1.0.0'
|
||||||
|
description: >
|
||||||
|
Data endpoints powering the analyst dashboard, portfolio-wide transaction review,
|
||||||
|
company search, and per-company transaction drill-down experiences.
|
||||||
|
servers:
|
||||||
|
- url: http://trusted_ai.test/api
|
||||||
|
description: Local development (composer run dev)
|
||||||
|
- url: https://api.trusted-ai.com
|
||||||
|
description: Production
|
||||||
|
tags:
|
||||||
|
- name: Dashboard
|
||||||
|
description: KPIs and alert overviews for the landing dashboard.
|
||||||
|
- name: Transactions
|
||||||
|
description: Portfolio-wide transaction review tooling.
|
||||||
|
- name: Companies
|
||||||
|
description: Company discovery and drill-down data.
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
sessionCookie:
|
||||||
|
type: apiKey
|
||||||
|
in: cookie
|
||||||
|
name: laravel_session
|
||||||
|
description: Browser session issued after login; required for all endpoints documented here.
|
||||||
|
schemas:
|
||||||
|
PaginationMeta:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- page
|
||||||
|
- perPage
|
||||||
|
- total
|
||||||
|
- lastPage
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 1
|
||||||
|
perPage:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 12
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
example: 128
|
||||||
|
lastPage:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
example: 11
|
||||||
|
CountAmountSummary:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- count
|
||||||
|
- amount
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
example: 42
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 2750000.5
|
||||||
|
description: Monetary volume in EUR unless noted otherwise.
|
||||||
|
StatusOption:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- value
|
||||||
|
- label
|
||||||
|
properties:
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
example: true_positive
|
||||||
|
label:
|
||||||
|
type: string
|
||||||
|
example: Bestätigte Treffer
|
||||||
|
ChannelOption:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- value
|
||||||
|
- label
|
||||||
|
properties:
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
example: SEPA
|
||||||
|
label:
|
||||||
|
type: string
|
||||||
|
example: SEPA
|
||||||
|
RiskSegment:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- label
|
||||||
|
- count
|
||||||
|
- volume
|
||||||
|
properties:
|
||||||
|
label:
|
||||||
|
type: string
|
||||||
|
example: Kritisch (≥80)
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
example: 17
|
||||||
|
volume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 8900000.0
|
||||||
|
TopCompanyAlert:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- companyId
|
||||||
|
- companyName
|
||||||
|
- ticker
|
||||||
|
- sector
|
||||||
|
- alerts
|
||||||
|
- alertVolume
|
||||||
|
- avgRiskScore
|
||||||
|
properties:
|
||||||
|
companyId:
|
||||||
|
type: integer
|
||||||
|
example: 24
|
||||||
|
companyName:
|
||||||
|
type: string
|
||||||
|
example: Allianz SE
|
||||||
|
ticker:
|
||||||
|
type: string
|
||||||
|
example: ALV
|
||||||
|
sector:
|
||||||
|
type: string
|
||||||
|
example: Insurance
|
||||||
|
alerts:
|
||||||
|
type: integer
|
||||||
|
example: 6
|
||||||
|
alertVolume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 1450000.0
|
||||||
|
avgRiskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 82
|
||||||
|
DashboardOverview:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- generatedAt
|
||||||
|
- activeAnalysts
|
||||||
|
- totalTransactions
|
||||||
|
- totalVolume
|
||||||
|
- alertsToday
|
||||||
|
- averageAlertsPerAnalyst
|
||||||
|
- precisionRate
|
||||||
|
- activeAlerts
|
||||||
|
- riskSegments
|
||||||
|
- topCompanies
|
||||||
|
- runbooksExecutedToday
|
||||||
|
properties:
|
||||||
|
generatedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2025-01-19T07:30:00Z'
|
||||||
|
activeAnalysts:
|
||||||
|
type: integer
|
||||||
|
example: 24
|
||||||
|
totalTransactions:
|
||||||
|
type: integer
|
||||||
|
example: 312
|
||||||
|
totalVolume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 98765432.1
|
||||||
|
alertsToday:
|
||||||
|
type: integer
|
||||||
|
example: 56
|
||||||
|
averageAlertsPerAnalyst:
|
||||||
|
type: integer
|
||||||
|
example: 3
|
||||||
|
precisionRate:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 84
|
||||||
|
activeAlerts:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- count
|
||||||
|
- highRiskCount
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
example: 128
|
||||||
|
highRiskCount:
|
||||||
|
type: integer
|
||||||
|
example: 19
|
||||||
|
riskSegments:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/RiskSegment'
|
||||||
|
topCompanies:
|
||||||
|
type: array
|
||||||
|
maxItems: 3
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TopCompanyAlert'
|
||||||
|
runbooksExecutedToday:
|
||||||
|
type: integer
|
||||||
|
example: 41
|
||||||
|
TransactionStatus:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- true_positive
|
||||||
|
- false_positive
|
||||||
|
- cleared
|
||||||
|
CompanySummary:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- legalName
|
||||||
|
- ticker
|
||||||
|
- sector
|
||||||
|
- country
|
||||||
|
- headquarters
|
||||||
|
- kycRiskLevel
|
||||||
|
- summary
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
example: 12
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
example: Allianz
|
||||||
|
legalName:
|
||||||
|
type: string
|
||||||
|
example: Allianz SE
|
||||||
|
ticker:
|
||||||
|
type: string
|
||||||
|
example: ALV
|
||||||
|
sector:
|
||||||
|
type: string
|
||||||
|
example: Insurance
|
||||||
|
country:
|
||||||
|
type: string
|
||||||
|
example: Germany
|
||||||
|
headquarters:
|
||||||
|
type: string
|
||||||
|
example: Munich
|
||||||
|
kycRiskLevel:
|
||||||
|
type: string
|
||||||
|
example: high
|
||||||
|
summary:
|
||||||
|
type: string
|
||||||
|
example: Multinational insurance provider with EMEA focus.
|
||||||
|
CompanyDetail:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/CompanySummary'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
createdAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
TransactionPreview:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- company
|
||||||
|
- counterparty
|
||||||
|
- counterpartyCountry
|
||||||
|
- status
|
||||||
|
- statusLabel
|
||||||
|
- requiresReview
|
||||||
|
- riskScore
|
||||||
|
- amount
|
||||||
|
- currency
|
||||||
|
- reference
|
||||||
|
- channel
|
||||||
|
- executedAt
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
example: 512
|
||||||
|
company:
|
||||||
|
$ref: '#/components/schemas/CompanySummary'
|
||||||
|
counterparty:
|
||||||
|
type: string
|
||||||
|
example: Alpine Holdings Ltd.
|
||||||
|
counterpartyCountry:
|
||||||
|
type: string
|
||||||
|
example: Switzerland
|
||||||
|
status:
|
||||||
|
$ref: '#/components/schemas/TransactionStatus'
|
||||||
|
statusLabel:
|
||||||
|
type: string
|
||||||
|
example: Bestätigter Treffer
|
||||||
|
requiresReview:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
|
riskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 87
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 245000.75
|
||||||
|
currency:
|
||||||
|
type: string
|
||||||
|
example: EUR
|
||||||
|
reference:
|
||||||
|
type: string
|
||||||
|
example: PAY-2024-10-1942
|
||||||
|
channel:
|
||||||
|
type: string
|
||||||
|
example: SWIFT
|
||||||
|
executedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2025-01-18T09:14:00Z'
|
||||||
|
flaggedReason:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
example: Counterparty on sanctions watchlist
|
||||||
|
TransactionSignal:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
- value
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
example: Adverse Media
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
example: Enforcement action reported in 2024-11
|
||||||
|
weight:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
nullable: true
|
||||||
|
example: 0.8
|
||||||
|
AlertSummary:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- counterparty
|
||||||
|
- executedAt
|
||||||
|
- channel
|
||||||
|
- status
|
||||||
|
- statusLabel
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
example: 911
|
||||||
|
counterparty:
|
||||||
|
type: string
|
||||||
|
example: Northbridge Trading Ltd.
|
||||||
|
executedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2025-01-18T14:52:00Z'
|
||||||
|
channel:
|
||||||
|
type: string
|
||||||
|
example: SWIFT
|
||||||
|
status:
|
||||||
|
$ref: '#/components/schemas/TransactionStatus'
|
||||||
|
statusLabel:
|
||||||
|
type: string
|
||||||
|
example: Bestätigter Treffer
|
||||||
|
riskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 92
|
||||||
|
TransactionHistoryItem:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- executedAt
|
||||||
|
- channel
|
||||||
|
- reference
|
||||||
|
- amount
|
||||||
|
- currency
|
||||||
|
- riskScore
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
example: 877
|
||||||
|
executedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2024-12-22T15:37:00Z'
|
||||||
|
channel:
|
||||||
|
type: string
|
||||||
|
example: SWIFT
|
||||||
|
reference:
|
||||||
|
type: string
|
||||||
|
example: PAY-2024-12-1187
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 99000.0
|
||||||
|
currency:
|
||||||
|
type: string
|
||||||
|
example: EUR
|
||||||
|
riskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 74
|
||||||
|
RecommendedAction:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
- description
|
||||||
|
properties:
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
example: Verdachtsmeldung vorbereiten
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
example: Erstellen Sie den Meldeentwurf für die FIU und sichern Sie Belege.
|
||||||
|
TransactionDetail:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/TransactionPreview'
|
||||||
|
- type: object
|
||||||
|
required:
|
||||||
|
- flaggedBy
|
||||||
|
- flaggedReason
|
||||||
|
- signals
|
||||||
|
properties:
|
||||||
|
flaggedBy:
|
||||||
|
type: string
|
||||||
|
example: Screening Engine
|
||||||
|
flaggedReason:
|
||||||
|
type: string
|
||||||
|
example: Counterparty matched to EU sanctions list
|
||||||
|
signals:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TransactionSignal'
|
||||||
|
TransactionCaseFile:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/TransactionDetail'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
recommendedActions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/RecommendedAction'
|
||||||
|
counterpartyHistory:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TransactionHistoryItem'
|
||||||
|
TransactionMetrics:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- total
|
||||||
|
- byStatus
|
||||||
|
properties:
|
||||||
|
total:
|
||||||
|
$ref: '#/components/schemas/CountAmountSummary'
|
||||||
|
byStatus:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/CountAmountSummary'
|
||||||
|
description: Breakdown keyed by transaction status.
|
||||||
|
CompanyTransactionMetrics:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- totalCount
|
||||||
|
- totalVolume
|
||||||
|
- openAlerts
|
||||||
|
- highRiskShare
|
||||||
|
- last30Days
|
||||||
|
- byStatus
|
||||||
|
properties:
|
||||||
|
totalCount:
|
||||||
|
type: integer
|
||||||
|
example: 96
|
||||||
|
totalVolume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 7200000.0
|
||||||
|
openAlerts:
|
||||||
|
$ref: '#/components/schemas/CountAmountSummary'
|
||||||
|
highRiskShare:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 28
|
||||||
|
last30Days:
|
||||||
|
$ref: '#/components/schemas/CountAmountSummary'
|
||||||
|
byStatus:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/CountAmountSummary'
|
||||||
|
TransactionCollectionResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- filters
|
||||||
|
- metrics
|
||||||
|
- data
|
||||||
|
- pagination
|
||||||
|
properties:
|
||||||
|
filters:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
statusOptions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/StatusOption'
|
||||||
|
perPage:
|
||||||
|
type: integer
|
||||||
|
example: 12
|
||||||
|
metrics:
|
||||||
|
$ref: '#/components/schemas/TransactionMetrics'
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TransactionPreview'
|
||||||
|
pagination:
|
||||||
|
$ref: '#/components/schemas/PaginationMeta'
|
||||||
|
selectedTransactionId:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
example: 512
|
||||||
|
selectedTransaction:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/TransactionDetail'
|
||||||
|
nullable: true
|
||||||
|
CompanySearchOverview:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- totalCompanies
|
||||||
|
- openAlerts
|
||||||
|
- openAlertVolume
|
||||||
|
- averageRiskScore
|
||||||
|
- watchlistHits
|
||||||
|
- automationShare
|
||||||
|
properties:
|
||||||
|
totalCompanies:
|
||||||
|
type: integer
|
||||||
|
example: 40
|
||||||
|
openAlerts:
|
||||||
|
type: integer
|
||||||
|
example: 112
|
||||||
|
openAlertVolume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 5640000.0
|
||||||
|
averageRiskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 71
|
||||||
|
watchlistHits:
|
||||||
|
type: integer
|
||||||
|
example: 9
|
||||||
|
automationShare:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 82
|
||||||
|
CompanyAlertPreview:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- transactionId
|
||||||
|
- counterparty
|
||||||
|
- executedAt
|
||||||
|
- channel
|
||||||
|
- flaggedReason
|
||||||
|
properties:
|
||||||
|
transactionId:
|
||||||
|
type: integer
|
||||||
|
example: 731
|
||||||
|
counterparty:
|
||||||
|
type: string
|
||||||
|
example: Baltic Commodities LLC
|
||||||
|
executedAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2025-01-17T11:48:00Z'
|
||||||
|
channel:
|
||||||
|
type: string
|
||||||
|
example: SWIFT
|
||||||
|
flaggedReason:
|
||||||
|
type: string
|
||||||
|
example: Pattern matches sanctions typology
|
||||||
|
riskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 88
|
||||||
|
CompanySearchResult:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/CompanySummary'
|
||||||
|
- type: object
|
||||||
|
required:
|
||||||
|
- alertCount
|
||||||
|
- alertVolume
|
||||||
|
- alertRiskScore
|
||||||
|
- latestAlert
|
||||||
|
properties:
|
||||||
|
alertCount:
|
||||||
|
type: integer
|
||||||
|
example: 7
|
||||||
|
alertVolume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
example: 980000.0
|
||||||
|
alertRiskScore:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
example: 79
|
||||||
|
latestAlert:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/CompanyAlertPreview'
|
||||||
|
CompanySearchResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- overview
|
||||||
|
- results
|
||||||
|
- meta
|
||||||
|
properties:
|
||||||
|
overview:
|
||||||
|
$ref: '#/components/schemas/CompanySearchOverview'
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/CompanySearchResult'
|
||||||
|
meta:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- query
|
||||||
|
- resultCount
|
||||||
|
- limit
|
||||||
|
properties:
|
||||||
|
query:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
example: Allianz
|
||||||
|
resultCount:
|
||||||
|
type: integer
|
||||||
|
example: 6
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
example: 6
|
||||||
|
CompanyTransactionsResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- company
|
||||||
|
- filters
|
||||||
|
- metrics
|
||||||
|
- data
|
||||||
|
- pagination
|
||||||
|
properties:
|
||||||
|
company:
|
||||||
|
$ref: '#/components/schemas/CompanyDetail'
|
||||||
|
filters:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
statusOptions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/StatusOption'
|
||||||
|
channelOptions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/ChannelOption'
|
||||||
|
metrics:
|
||||||
|
$ref: '#/components/schemas/CompanyTransactionMetrics'
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TransactionPreview'
|
||||||
|
pagination:
|
||||||
|
$ref: '#/components/schemas/PaginationMeta'
|
||||||
|
selectedTransactionId:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
example: 877
|
||||||
|
selectedTransaction:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/TransactionCaseFile'
|
||||||
|
nullable: true
|
||||||
|
recentAlerts:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/AlertSummary'
|
||||||
|
paths:
|
||||||
|
/dashboard/overview:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Dashboard
|
||||||
|
operationId: getDashboardOverview
|
||||||
|
summary: Retrieve dashboard overview metrics
|
||||||
|
description: >
|
||||||
|
Aggregated KPIs and alert slices needed for the dashboard hero cards and side panels.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Overview data ready for dashboard rendering.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/DashboardOverview'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
/transactions:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Transactions
|
||||||
|
operationId: listTransactions
|
||||||
|
summary: List transactions with global filters
|
||||||
|
description: >
|
||||||
|
Returns the paginated transaction stream together with per-status metrics and default selection,
|
||||||
|
mirroring the Livewire transaction review experience.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
parameters:
|
||||||
|
- name: search
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Match against reference, counterparty, or company identifiers.
|
||||||
|
- name: status
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- true_positive
|
||||||
|
- false_positive
|
||||||
|
- cleared
|
||||||
|
default: all
|
||||||
|
description: Filter by review status; `all` keeps every status.
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
default: 1
|
||||||
|
- name: perPage
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 100
|
||||||
|
default: 12
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Paginated transaction set with metrics.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TransactionCollectionResponse'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
/transactions/{transactionId}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Transactions
|
||||||
|
operationId: getTransactionCaseFile
|
||||||
|
summary: Fetch a single transaction case file
|
||||||
|
description: >
|
||||||
|
Detailed transaction record used when an analyst expands a case in the transaction review workspace.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
parameters:
|
||||||
|
- name: transactionId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Transaction detail with detection signals.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TransactionDetail'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
'404':
|
||||||
|
description: Transaction not found.
|
||||||
|
/companies/search:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Companies
|
||||||
|
operationId: searchCompanies
|
||||||
|
summary: Search companies and retrieve screening overview
|
||||||
|
description: >
|
||||||
|
Provides the metrics and top results required to populate the company search tiles.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
parameters:
|
||||||
|
- name: q
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Free-text query across name, legal name, ticker, or sector.
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 25
|
||||||
|
default: 6
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Matching companies with alert context.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CompanySearchResponse'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
/companies/{companyId}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Companies
|
||||||
|
operationId: getCompany
|
||||||
|
summary: Retrieve a company profile
|
||||||
|
description: >
|
||||||
|
Returns core firmographics and KYC risk tier needed for per-company transaction pages.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
parameters:
|
||||||
|
- name: companyId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Company detail.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CompanyDetail'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
'404':
|
||||||
|
description: Company not found.
|
||||||
|
/companies/{companyId}/transactions:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Companies
|
||||||
|
operationId: listCompanyTransactions
|
||||||
|
summary: List transactions for a specific company
|
||||||
|
description: >
|
||||||
|
Supplies per-company metrics, channel/status filters, paginated cases, and the current selection
|
||||||
|
for the company drill-down view.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
parameters:
|
||||||
|
- name: companyId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: status
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- true_positive
|
||||||
|
- false_positive
|
||||||
|
- cleared
|
||||||
|
default: all
|
||||||
|
- name: channel
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
default: all
|
||||||
|
description: Channel label returned in channelOptions; use `all` for no filtering.
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
default: 1
|
||||||
|
- name: perPage
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 100
|
||||||
|
default: 10
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Company-specific transaction data with metrics.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CompanyTransactionsResponse'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
'404':
|
||||||
|
description: Company not found or no transactions.
|
||||||
|
/companies/{companyId}/transactions/{transactionId}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Companies
|
||||||
|
operationId: getCompanyTransactionCaseFile
|
||||||
|
summary: Fetch a company transaction case file with history
|
||||||
|
description: >
|
||||||
|
Returns the selected transaction enriched with recommended actions and counterparty history
|
||||||
|
used in the right-hand panel of the company drill-down.
|
||||||
|
security:
|
||||||
|
- sessionCookie: []
|
||||||
|
parameters:
|
||||||
|
- name: companyId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: transactionId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Transaction case file for the company context.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/TransactionCaseFile'
|
||||||
|
'401':
|
||||||
|
description: Session invalid or expired.
|
||||||
|
'404':
|
||||||
|
description: Transaction not found for the given company.
|
||||||
Reference in New Issue
Block a user