Metadata-Version: 2.4
Name: boxdrive
Version: 0.0.1
Summary: S3-compatible API with Abstract Object Store in Python (FastAPI).
Author-email: Alex Severin <severinalexeyv@gmail.com>
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: fastapi[standard]>=0.115.14
Requires-Dist: httpx>=0.24.0
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.55b1
Requires-Dist: opentelemetry-instrumentation-logging>=0.55b1
Requires-Dist: opentelemetry-sdk>=1.34.1
Requires-Dist: pydantic-xml>=2.17.2
Requires-Dist: pydantic>=2.11.7
Description-Content-Type: text/markdown

# BoxDrive
[![github]](https://github.com/cospectrum/boxdrive)
[![ci]](https://github.com/cospectrum/boxdrive/actions)

[github]: https://img.shields.io/badge/github-cospectrum/boxdrive-8da0cb?logo=github
[ci]: https://github.com/cospectrum/boxdrive/workflows/ci/badge.svg

S3-compatible API with **Abstract Object Store** in Python (FastAPI).
Work in progress.

## Installation

```bash
pip install -e git+https://github.com/cospectrum/boxdrive.git
```

## Quick Start

### Basic Usage

1. create `main.py`:
```python
from boxdrive import create_app
from boxdrive.stores import InMemoryStore

store = InMemoryStore()
app = create_app(store)
```

2. start API in dev mode:
```bash
fastapi dev main.py
```
The API will be available at `http://localhost:8000` with automatic documentation at `http://localhost:8000/docs`.

## API Endpoints

The API provides S3-compatible endpoints:

- `GET /` - List buckets
- `PUT /{bucket}` - Create a bucket
- `DELETE /{bucket}` - Delete a bucket
- `GET /{bucket}` - List objects in a bucket
- `GET /{bucket}/{key}` - Get an object
- `PUT /{bucket}/{key}` - Put an object
- `DELETE /{bucket}/{key}` - Delete an object

## Creating Custom Object Stores

To create a custom object store implementation, inherit from `ObjectStore`:

```python
from boxdrive import (
    BucketInfo,
    BucketName,
    ContentType,
    Key,
    ListObjectsInfo,
    ListObjectsV2Info,
    MaxKeys,
    Object,
    ObjectInfo,
    ObjectStore,
)

class MyCustomStore(ObjectStore):
    async def list_buckets(self) -> list[BucketInfo]: ...
    async def create_bucket(self, bucket_name: BucketName) -> None: ...
    async def delete_bucket(self, bucket_name: BucketName) -> None: ...
    async def get_object(self, bucket_name: BucketName, key: Key) -> Object | None: ...
    async def put_object(
        self, bucket_name: BucketName, key: Key, data: bytes, content_type: ContentType | None = None
    ) -> ObjectInfo: ...
    async def delete_object(self, bucket_name: BucketName, key: Key) -> ObjectInfo: ...
    async def head_object(self, bucket_name: BucketName, key: Key) -> ObjectInfo | None: ...
    async def list_objects(
        self,
        bucket_name: BucketName,
        *,
        prefix: Key | None = None,
        delimiter: str | None = None,
        max_keys: MaxKeys = 1000,
        marker: Key | None = None,
    ) -> ListObjectsInfo: ...
    async def list_objects_v2(
        self,
        bucket_name: BucketName,
        *,
        continuation_token: Key | None = None,
        delimiter: str | None = None,
        encoding_type: str | None = None,
        max_keys: MaxKeys = 1000,
        prefix: Key | None = None,
        start_after: Key | None = None,
    ) -> ListObjectsV2Info: ...
```

## Development

### Running Tests

unit:
```bash
uv run pytest/unit
```

e2e:
```bash
# start server
uv run fastapi dev src/boxdrive/main.py --port 8000
export S3_ENDPOINT_URL=http://127.0.0.1:8000

# run e2e tests
uv run run pytest/e2e
```

### Code Quality

```bash
uv run ruff format .
uv run ruff check . --fix
uv run mypy .
```

## License

Apache 2.0 - see [LICENSE](./LICENSE) file for details.
