Documentation Index
Fetch the complete documentation index at: https://mintlify.com/IvanchoDev89/maleku-system/llms.txt
Use this file to discover all available pages before exploring further.
Maleku System is built for production observability from day one. The FastAPI backend exposes a Prometheus scrape endpoint, ships pre-provisioned Grafana dashboards and datasource configs, uses structured JSON logging throughout, and records every state-changing operation in a persistent audit log. Together these layers give you metrics, logs, and a full audit trail without additional instrumentation.
Prometheus Metrics
In development, the Prometheus metrics endpoint is available at http://localhost:8000/metrics. In production on Railway, the endpoint is at https://your-backend.up.railway.app/metrics.
The FastAPI application exposes a GET /metrics endpoint in the standard Prometheus text exposition format. Prometheus is configured to scrape it every 10 seconds:
# infraestructura/prometheus/prometheus.yml
scrape_configs:
- job_name: 'costarica-backend'
static_configs:
- targets: ['backend:8000']
metrics_path: '/metrics'
scrape_interval: 10s
Prometheus also scrapes sidecar exporters for the database and cache layers:
| Job | Target | Scrape interval | What it monitors |
|---|
costarica-backend | backend:8000 | 10 s | FastAPI request counts, response times, error rates |
postgres | postgres:9187 | 15 s | PostgreSQL connections, query latency, cache hit ratio |
redis | redis:9121 | 15 s | Redis memory, hit/miss rate, connected clients |
Start the monitoring stack with the monitoring Docker Compose profile:
docker compose --profile monitoring up -d
Prometheus will be reachable at http://localhost:9090.
Grafana Dashboards
Grafana is provisioned automatically from the infraestructura/grafana/ directory. No manual dashboard imports are required — everything is loaded from files on container start.
Datasource provisioning (infraestructura/grafana/datasources/datasource.yml):
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
jsonData:
timeInterval: 15s
Dashboard provisioning (infraestructura/grafana/dashboards/dashboard.yml) configures Grafana to load all JSON dashboard files from the same directory:
apiVersion: 1
providers:
- name: 'Default'
orgId: 1
folder: ''
type: file
disableDeletion: true
editable: true
options:
path: /etc/grafana/provisioning/dashboards
Grafana is available at http://localhost:3002 when started via Docker Compose. The default admin password is controlled by the GRAFANA_PASSWORD environment variable (defaults to admin if not set).
To add a new dashboard, export it as JSON from Grafana, save the file to infraestructura/grafana/dashboards/, and restart the Grafana container. It will be auto-provisioned on next start.
Structured Logging
All backend modules obtain a logger with get_logger(__name__) from app.core.logging. The logging system supports two output formats controlled by environment variables:
LOG_FORMAT value | Formatter used | Best for |
|---|
| (empty) | SimpleFormatter — coloured, human-readable | Local development |
json | JSONFormatter — machine-parseable | Production / log aggregators |
JSON log entry shape (production):
{
"timestamp": "2024-11-15T10:23:41.123456+00:00",
"level": "INFO",
"logger": "app.api.v1.bookings",
"message": "Booking created for tour_id=abc123",
"request": {
"method": "POST",
"path": "/api/v1/bookings",
"status_code": 201,
"duration_ms": 84.37
}
}
For WARNING and above, the entry also includes a source block with file, line, and function to pinpoint the origin without a stack trace.
Privacy — the JSONFormatter and SimpleFormatter both redact email addresses in log messages automatically (e.g. user@example.com → u***r@example.com), preventing accidental PII leakage in log aggregators.
Configure logging with:
LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR | CRITICAL
LOG_FORMAT=json # empty for coloured dev output; "json" for production
Rate Limiting Observability
Maleku System uses SlowAPI (a Starlette/FastAPI port of Flask-Limiter) backed by Redis so rate-limit counters are shared across all Uvicorn workers.
Rate limits are role-based — authenticated users get higher allowances keyed by user_id, while anonymous requests share per-IP buckets:
| Role | Default limit |
|---|
| Anonymous | 30 requests / minute |
| Client | 60 requests / minute |
| Vendor | 100 requests / minute |
| Admin | 200 requests / minute |
| Super Admin | 300 requests / minute |
Specific endpoints carry tighter per-endpoint overrides:
| Endpoint | Limit |
|---|
POST /api/v1/auth/login | 5 / minute |
POST /api/v1/bookings | 10 / minute |
POST /api/v1/availability | 10 / minute |
GET /api/v1/availability | 30 / minute |
GET /api/v1/blog | 30 / minute |
| Admin write endpoints | 10 / minute |
When a request is rejected (HTTP 429), the rate limiter logs a warning via get_logger(__name__):
Rate limit hit on POST /api/v1/bookings from 203.0.113.42
The response includes a Retry-After header so clients know when to retry. The X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers are injected on every response when headers_enabled=True (the default).
Audit Logging
AuditLog records every state-changing operation in the audit_logs PostgreSQL table. The table is indexed for common query patterns (by user_id + action, by entity_type + entity_id, and by created_at DESC).
Accessing audit logs — superadmins can query the audit trail via:
GET /api/v1/superadmin/audit
Each AuditLog record contains the following fields:
| Field | Type | Description |
|---|
id | UUID | Unique log entry identifier |
user_id | UUID | User who performed the action (nullable) |
user_email | string | Snapshot of the email at time of action |
action | AuditAction enum | One of: create, update, delete, login, logout, approve, reject, suspend, activate, payment, refund, cancel, confirm, complete, and more |
entity_type | string | Resource type: user, vendor, booking, property, etc. |
entity_id | UUID | ID of the affected resource |
entity_name | string | Human-readable name of the resource |
old_values | JSONB | State before the change |
new_values | JSONB | State after the change |
changes_summary | text | Human-readable diff |
ip_address | INET | Client IP address |
user_agent | string | Client User-Agent header |
request_path | string | API path that triggered the action |
created_at | timestamptz | UTC timestamp of the event |
Security events are recorded in a separate security_logs table (SecurityLog model) with a severity field (info, warning, critical) and structured details JSONB. Tracked security actions include login_success, login_failure, password_change, rate_limit_hit, suspicious_activity, account_locked, and more.
Security logs are accessible via:
GET /api/v1/superadmin/audit/security