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
|
||||
+12
-4
@@ -24,9 +24,11 @@
|
||||
# Provide php alias
|
||||
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
|
||||
RUN addgroup -S web && adduser -S web -G web \
|
||||
&& mkdir -p /run/nginx /run/php /var/www/html /var/log/supervisor
|
||||
RUN mkdir -p /run/nginx /run/php /var/www/html /var/log/supervisor
|
||||
|
||||
# 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 \
|
||||
@@ -51,8 +53,14 @@
|
||||
# Permissions for Laravel writable directories
|
||||
RUN mkdir -p 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
|
||||
RUN npm ci && npm run build
|
||||
|
||||
|
||||
+77
-16
@@ -1,27 +1,88 @@
|
||||
#!/bin/sh
|
||||
# Docker entrypoint for Nginx + PHP/Laravel
|
||||
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
|
||||
|
||||
# Ensure dirs
|
||||
mkdir -p /etc/nginx/http.d /etc/nginx/conf.d /run/nginx /run/php
|
||||
# truthy helper
|
||||
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
|
||||
if [ -f /etc/nginx/templates/default.conf.template ]; then
|
||||
envsubst '$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/http.d/default.conf
|
||||
# -------- basic auth directives --------
|
||||
BASIC_AUTH_DIRECTIVES=""
|
||||
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
|
||||
echo "[entrypoint] ERROR: missing /etc/nginx/templates/default.conf.template" >&2
|
||||
exit 1
|
||||
log "Basic authentication is disabled"
|
||||
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
|
||||
|
||||
echo "[entrypoint] Using SERVER_NAME=$SERVER_NAME"
|
||||
nginx -t || { echo "[entrypoint] nginx config test failed" >&2; cat /var/log/nginx/error.log || true; exit 1; }
|
||||
# -------- entrypoint behavior --------
|
||||
# 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
|
||||
php artisan cache:clear
|
||||
# If they provided a command (e.g., sh, bash, php -v), run it.
|
||||
if [ "${1:-}" ] && [ "$1" != "/usr/bin/supervisord" ] && [ "$1" != "supervisord" ]; then
|
||||
log "Executing custom command: $*"
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
envsubst '$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/http.d/default.conf
|
||||
|
||||
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;
|
||||
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 {
|
||||
worker_connections 1024;
|
||||
@@ -15,14 +17,13 @@ http {
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /dev/stdout main;
|
||||
error_log /dev/stderr warn;
|
||||
access_log /proc/self/fd/1 main;
|
||||
# (No extra error_log here; the top-level one above is enough)
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# Server Blöcke sind NICHT hier!
|
||||
# Stattdessen in:
|
||||
# Include vhosts (your Laravel server block lives in one of these)
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/http.d/*.conf;
|
||||
}
|
||||
|
||||
@@ -5,42 +5,89 @@ server {
|
||||
root /var/www/html/public;
|
||||
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;
|
||||
add_header Cache-Control "public, max-age=604800, immutable";
|
||||
try_files $uri =404;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Haupt-Entry-Point (Laravel)
|
||||
# -------------------------------------------------
|
||||
# Main Laravel entry point
|
||||
# -------------------------------------------------
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
${BASIC_AUTH_DIRECTIVES}
|
||||
}
|
||||
|
||||
# PHP-FPM Verarbeitung
|
||||
# -------------------------------------------------
|
||||
# PHP-FPM configuration (matches your Alpine socket setup)
|
||||
# -------------------------------------------------
|
||||
location ~ \.php$ {
|
||||
include fastcgi_params;
|
||||
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 DOCUMENT_ROOT $document_root;
|
||||
|
||||
fastcgi_read_timeout 60s;
|
||||
fastcgi_buffering on;
|
||||
fastcgi_buffers 16 16k;
|
||||
fastcgi_buffer_size 32k;
|
||||
|
||||
# Forward the X-Forwarded-Proto header
|
||||
fastcgi_param HTTPS $http_x_forwarded_proto;
|
||||
# --- Proxy headers for HTTPS and load balancers ---
|
||||
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;
|
||||
|
||||
# Sicherheits-Header
|
||||
# -------------------------------------------------
|
||||
# Security headers
|
||||
# -------------------------------------------------
|
||||
add_header X-Frame-Options "SAMEORIGIN" 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