# Object Storage API

Manage S3-compatible object storage buckets and access keys programmatically through the DanubeData API.

## Endpoints Overview

### Storage Buckets

| Method | Endpoint | Description | Scope Required |
|--------|----------|-------------|----------------|
| GET | `/api/v1/storage/buckets` | List all buckets | `storage:read` |
| POST | `/api/v1/storage/buckets` | Create a new bucket | `storage:write` |
| GET | `/api/v1/storage/buckets/{id}` | Get bucket details | `storage:read` |
| PUT | `/api/v1/storage/buckets/{id}` | Update bucket settings | `storage:write` |
| DELETE | `/api/v1/storage/buckets/{id}` | Delete a bucket | `storage:delete` |
| GET | `/api/v1/storage/buckets/{id}/metrics` | Get bucket metrics | `storage:read` |
| GET | `/api/v1/storage/buckets/{id}/metrics/trend` | Historical time-series metrics | `storage:read` |
| GET | `/api/v1/storage/buckets/{id}/metrics/top-objects` | Top objects by size / egress / requests | `storage:read` |
| GET | `/api/v1/storage/buckets/{id}/metrics/health` | Bucket health (multipart, deleted versions) | `storage:read` |

### Storage Access Keys

| Method | Endpoint | Description | Scope Required |
|--------|----------|-------------|----------------|
| GET | `/api/v1/storage/access-keys` | List all access keys | `storage:read` |
| POST | `/api/v1/storage/access-keys` | Create a new access key | `storage:write` |
| GET | `/api/v1/storage/access-keys/{id}` | Get access key details | `storage:read` |
| DELETE | `/api/v1/storage/access-keys/{id}` | Revoke an access key | `storage:delete` |

## Storage Buckets

### List All Buckets

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/buckets' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "data": [
    {
      "id": "9c8b7a6e-5d4c-3b2a-1098-76543210fedc",
      "name": "my-bucket",
      "display_name": "My Application Bucket",
      "status": "active",
      "status_label": "Active",
      "region": "fsn1",
      "endpoint_url": "https://s3.danubedata.ro",
      "public_access": false,
      "versioning_enabled": false,
      "encryption_enabled": true,
      "size_bytes": 1073741824,
      "size_human": "1.00 GB",
      "object_count": 150,
      "monthly_cost_cents": 500,
      "monthly_cost_dollars": 5.00,
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 15,
    "total": 1
  }
}
```

### Create a Bucket

```bash
curl -X POST 'https://danubedata.ro/api/v1/storage/buckets' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "name": "my-new-bucket",
    "display_name": "My New Bucket",
    "region": "fsn1",
    "versioning_enabled": false,
    "public_access": false,
    "encryption_enabled": true,
    "tags": {
      "environment": "production",
      "project": "webapp"
    }
  }'
```

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Bucket name (3-63 chars, lowercase, alphanumeric and hyphens) |
| `display_name` | string | No | Human-readable display name |
| `region` | string | Yes | Region code (e.g., `fsn1`) |
| `versioning_enabled` | boolean | No | Enable object versioning (default: false) |
| `public_access` | boolean | No | Allow public read access (default: false) |
| `encryption_enabled` | boolean | No | Enable server-side encryption (default: true) |
| `encryption_type` | string | No | Encryption type: `sse-s3` or `sse-kms` |
| `cors_configuration` | array | No | CORS rules for the bucket |
| `lifecycle_rules` | array | No | Object lifecycle rules |
| `tags` | object | No | Key-value tags for the bucket |

**Response (201 Created):**
```json
{
  "message": "Storage bucket created successfully",
  "bucket": {
    "id": "9c8b7a6e-5d4c-3b2a-1098-76543210fedc",
    "name": "my-new-bucket",
    "status": "pending",
    "status_label": "Pending"
  }
}
```

### Get Bucket Details

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "bucket": {
    "id": "9c8b7a6e-5d4c-3b2a-1098-76543210fedc",
    "name": "my-bucket",
    "display_name": "My Application Bucket",
    "status": "active",
    "region": "fsn1",
    "endpoint_url": "https://s3.danubedata.ro",
    "public_url": null,
    "public_access": false,
    "versioning_enabled": false,
    "encryption_enabled": true,
    "encryption_type": "sse-s3",
    "size_bytes": 1073741824,
    "object_count": 150,
    "tags": {
      "environment": "production"
    },
    "monthly_cost_cents": 500,
    "monthly_cost_dollars": 5.00,
    "can_be_modified": true,
    "can_be_destroyed": true,
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T10:30:00Z"
  },
  "endpoint": "https://s3.danubedata.ro"
}
```

### Update Bucket Settings

```bash
curl -X PUT 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "display_name": "Updated Bucket Name",
    "versioning_enabled": true,
    "public_access": false,
    "tags": {
      "environment": "staging"
    }
  }'
```

**Response:**
```json
{
  "message": "Bucket settings updated successfully",
  "bucket": {
    "id": "9c8b7a6e-5d4c-3b2a-1098-76543210fedc",
    "name": "my-bucket",
    "display_name": "Updated Bucket Name",
    "versioning_enabled": true
  }
}
```

### Delete a Bucket

```bash
curl -X DELETE 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "message": "Bucket deletion initiated",
  "status": "destroying"
}
```

**Note:** Bucket deletion is asynchronous. The bucket and all its objects will be permanently deleted.

### Get Bucket Metrics

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "size_bytes": 1073741824,
  "size_human": "1.00 GB",
  "object_count": 150,
  "requests_24h": 48213,
  "requests_24h_by_method": { "GET": 41002, "PUT": 6100, "DELETE": 1111 },
  "requests_24h_by_status": { "2xx": 47800, "4xx": 390, "5xx": 23 },
  "error_rate_24h": 0.0086,
  "latency_24h_ms": { "p50": 12.4, "p95": 58.1, "mean": 18.7 },
  "egress_bytes_24h": 5368709120,
  "egress_human_24h": "5.00 GB",
  "ingress_bytes_24h": 1073741824,
  "ingress_human_24h": "1.00 GB",
  "monthly_cost_cents": 500,
  "monthly_cost_dollars": 5.00,
  "source": "deltas",
  "freshness": "fresh",
  "last_sync_at": "2024-01-15T12:00:00Z",
  "metrics_precomputed_at": "2024-01-15T12:00:00Z"
}
```

Notes:
- **`freshness`** is `fresh`, `lagging`, or `stale` — how recent the metrics are.
- **`source`** is `deltas` when the per-interval metrics pipeline has data for the bucket, or `legacy` for older buckets where the status/latency/ingress fields are `null`. Skip absent dimensions when `source` is `legacy`.

### Get Metrics Trend

Historical time-series for a bucket.

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics/trend?window=24h' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Query parameters:**

| Parameter | Values | Default | Description |
|-----------|--------|---------|-------------|
| `window` | `1h`, `6h`, `12h`, `24h`, `3d`, `7d`, `14d`, `30d` | `24h` | Time range |
| `resolution` | `1m`, `5m`, `10m`, `15m`, `30m`, `1h`, `6h`, `1d` | auto (from `window`) | Bucket interval for each data point |

**Response:**
```json
{
  "bucket_id": "01HXYZ...",
  "window": "24h",
  "resolution": "5m",
  "source": "deltas",
  "freshness": "fresh",
  "generated_at": "2024-01-15T12:00:00Z",
  "data": [ ... ]
}
```

### Get Top Objects

Top-N objects in a bucket by size, egress, or request count. Powered by an hourly probe — returns an empty `items` array until the first snapshot has run.

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics/top-objects?dimension=size&limit=10' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Query parameters:**

| Parameter | Values | Default | Description |
|-----------|--------|---------|-------------|
| `dimension` | `size`, `egress`, `requests` | `size` | Ranking dimension |
| `limit` | `1`–`50` | `10` | Number of objects to return |

**Response:**
```json
{
  "bucket_id": "01HXYZ...",
  "dimension": "size",
  "recorded_at": "2024-01-15T11:00:00Z",
  "items": [
    { "rank": 1, "object_key": "backups/2024-01-15.tar.gz", "value": 2147483648 },
    { "rank": 2, "object_key": "media/video.mp4", "value": 734003200 }
  ]
}
```

### Get Bucket Health

Reclaimable-space and freshness indicators. Values are `null` until the hourly health probe has visited the bucket.

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/buckets/{bucket_id}/metrics/health' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "bucket_id": "01HXYZ...",
  "pending_multipart_count": 3,
  "pending_multipart_bytes": 15728640,
  "deleted_size_bytes": 524288000,
  "freshness": "fresh",
  "metrics_precomputed_at": "2024-01-15T12:00:00Z",
  "last_health_check_at": "2024-01-15T11:00:00Z"
}
```

## Storage Access Keys

Access keys provide S3-compatible credentials for accessing your storage buckets programmatically.

### List All Access Keys

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/access-keys' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "data": [
    {
      "id": "abc12345-6789-0abc-def1-234567890abc",
      "name": "Production API Key",
      "access_key_id": "DDAK1234567890EXAMPLE",
      "access_key_id_masked": "DDAK****XAMPLE",
      "status": "active",
      "status_label": "Active",
      "permissions": null,
      "expires_at": null,
      "last_used_at": "2024-01-15T11:30:00Z",
      "is_expired": false,
      "created_at": "2024-01-10T09:00:00Z",
      "updated_at": "2024-01-15T11:30:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 15,
    "total": 1
  }
}
```

### Create an Access Key

```bash
curl -X POST 'https://danubedata.ro/api/v1/storage/access-keys' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "name": "My Application Key",
    "expires_at": "2025-01-15T00:00:00Z"
  }'
```

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | A descriptive name for the access key |
| `expires_at` | datetime | No | Expiration date (ISO 8601 format) |

**Response (201 Created):**
```json
{
  "id": "abc12345-6789-0abc-def1-234567890abc",
  "name": "My Application Key",
  "access_key_id": "DDAK1234567890EXAMPLE",
  "secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "expires_at": "2025-01-15T00:00:00Z",
  "message": "Access key created. Make sure to save the secret key - it will not be shown again."
}
```

**Important:** The `secret_access_key` is only returned once during creation. Store it securely as it cannot be retrieved later.

### Get Access Key Details

```bash
curl -X GET 'https://danubedata.ro/api/v1/storage/access-keys/{access_key_id}' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "access_key": {
    "id": "abc12345-6789-0abc-def1-234567890abc",
    "name": "My Application Key",
    "access_key_id": "DDAK1234567890EXAMPLE",
    "access_key_id_masked": "DDAK****XAMPLE",
    "status": "active",
    "status_label": "Active",
    "expires_at": "2025-01-15T00:00:00Z",
    "last_used_at": "2024-01-15T11:30:00Z",
    "is_expired": false,
    "created_at": "2024-01-10T09:00:00Z"
  }
}
```

**Note:** The `secret_access_key` is never returned after initial creation.

### Revoke an Access Key

```bash
curl -X DELETE 'https://danubedata.ro/api/v1/storage/access-keys/{access_key_id}' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'
```

**Response:**
```json
{
  "message": "Access key has been revoked"
}
```

**Warning:** Revoking an access key immediately invalidates it. Any applications using this key will lose access.

## Using Access Keys with S3 Clients

Once you have created an access key, you can use it with any S3-compatible client.

### AWS CLI

```bash
aws configure --profile danubedata

# Enter your credentials when prompted:
# AWS Access Key ID: DDAK1234567890EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name: fsn1

# Use with custom endpoint
aws s3 ls s3://my-bucket \
  --endpoint-url https://s3.danubedata.ro \
  --profile danubedata
```

### Python (boto3)

```python
import boto3

s3 = boto3.client(
    's3',
    endpoint_url='https://s3.danubedata.ro',
    aws_access_key_id='DDAK1234567890EXAMPLE',
    aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
    region_name='fsn1'
)

# List objects in a bucket
response = s3.list_objects_v2(Bucket='my-bucket')
for obj in response.get('Contents', []):
    print(obj['Key'])
```

### JavaScript (AWS SDK v3)

```javascript
import { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3';

const s3 = new S3Client({
  endpoint: 'https://s3.danubedata.ro',
  region: 'fsn1',
  credentials: {
    accessKeyId: 'DDAK1234567890EXAMPLE',
    secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
  },
  forcePathStyle: true
});

const command = new ListObjectsV2Command({ Bucket: 'my-bucket' });
const response = await s3.send(command);
console.log(response.Contents);
```

## Error Responses

### 422 Unprocessable Entity

```json
{
  "error": "You have reached the maximum number of buckets (10). Please delete a bucket to create a new one."
}
```

```json
{
  "error": "Bucket cannot be modified in its current state",
  "status": "creating"
}
```

### 404 Not Found

```json
{
  "error": "Bucket not found"
}
```

### 403 Forbidden

Returned when your API token lacks the required `storage:*` scope.

```json
{
  "message": "Insufficient permissions"
}
```

## Rate Limits

Storage API endpoints have specific rate limits:

| Endpoint | Limit |
|----------|-------|
| Create bucket | 40/min, 200/hour, 2000/day |
| Create access key | 20/min, 100/hour, 1000/day |
| Get metrics | 2x standard limits |
| Other endpoints | Standard tier limits |

See [Rate Limits](https://docs.danubedata.ro/api-rate-limits) for more details.
