# Quick Start

This page shows the two most common setups — local filesystem and AWS S3 — so you can be productive in minutes.

## Local Filesystem (Nginx)

Good for development, single-server deployments, or any setup where a static web server (Nginx, Apache, Caddy …) serves files directly from disk.

    from granite_assets import (
        LocalNginxAssetRepositoryConfig,
        LocalNginxAssetRepository,
        AssetSaveRequest,
        AssetVisibility,
    )

    # 1. Configure the repository.
    config = LocalNginxAssetRepositoryConfig(
        storage_path="/var/www/assets",      # absolute path on disk
        base_url="https://cdn.example.com/assets",  # how Nginx serves it
        public_prefix="public",             # default; public files go here
        private_prefix="private",           # private files (Nginx-protected)
    )
    repo = LocalNginxAssetRepository(config)

    # 2. Save an asset.
    with open("photo.jpg", "rb") as f:
        result = repo.save(AssetSaveRequest(
            key="avatars/user-42.jpg",
            source=f,
            content_type="image/jpeg",
            visibility=AssetVisibility.PUBLIC,
            filename="photo.jpg",
        ))
    print(result.key)            # avatars/user-42.jpg
    print(result.content_length) # bytes written

    # 3. Build a public URL.
    url = repo.build_public_url("avatars/user-42.jpg")
    print(url.url)   # https://cdn.example.com/assets/public/avatars/user-42.jpg
    print(url.is_permanent)  # True

    # 4. Check existence.
    assert repo.exists("avatars/user-42.jpg")

    # 5. Get metadata (no download).
    descriptor = repo.get_descriptor("avatars/user-42.jpg")
    print(descriptor.content_length)
    print(descriptor.last_modified)

    # 6. Copy and move.
    repo.copy("avatars/user-42.jpg", "avatars/backup/user-42.jpg")
    repo.move("avatars/backup/user-42.jpg", "archive/user-42.jpg")

    # 7. Delete.
    repo.delete("avatars/user-42.jpg")

## AWS S3

Replace the configuration object; the API is identical.

    from granite_assets import (
        S3AssetRepositoryConfig,
        AssetSaveRequest,
        AssetVisibility,
        build_asset_repository,
    )

    config = S3AssetRepositoryConfig(
        bucket="my-app-assets",
        region="eu-west-1",
        key_prefix="production/",           # optional prefix inside the bucket
        public_base_url="https://cdn.example.com",  # CDN in front of the bucket
        presign_ttl_seconds=3600,           # default TTL for signed URLs
    )
    repo = build_asset_repository(config)   # returns S3AssetRepository

    # Save a public asset.
    with open("banner.png", "rb") as f:
        result = repo.save(AssetSaveRequest(
            key="banners/homepage.png",
            source=f,
            content_type="image/png",
            visibility=AssetVisibility.PUBLIC,
        ))

    # Permanent public URL (via CDN).
    url = repo.build_public_url("banners/homepage.png")
    print(url.url)   # https://cdn.example.com/banners/homepage.png

    # Save a private asset.
    with open("invoice.pdf", "rb") as f:
        repo.save(AssetSaveRequest(
            key="invoices/inv-001.pdf",
            source=f,
            content_type="application/pdf",
            visibility=AssetVisibility.PRIVATE,
        ))

    # Time-limited download URL.
    dl = repo.build_download_url("invoices/inv-001.pdf", ttl_seconds=300)
    print(dl.url)        # https://... (presigned S3 URL)
    print(dl.expires_at) # UTC datetime 5 minutes from now

## Using the Factory

`build_asset_repository` inspects the config type and returns the right repository. This is useful in dependency-injection setups:

    from granite_assets import build_asset_repository, LocalNginxAssetRepositoryConfig

    def get_repo():
        # In production, read from environment / settings
        config = LocalNginxAssetRepositoryConfig(
            storage_path="/var/www/assets",
            base_url="http://localhost/assets",
        )
        return build_asset_repository(config)

## Error Handling

    from granite_assets import (
        AssetError,
        AssetNotFoundError,
        AssetAccessNotSupportedError,
    )

    try:
        repo.delete("missing-key.jpg")
    except AssetNotFoundError as e:
        print(f"Key not found: {e.key}")

    try:
        repo.build_upload_url("some/key.jpg", "image/jpeg")
    except AssetAccessNotSupportedError as e:
        print(f"{e.backend}: {e.operation} not supported")

    try:
        repo.save(bad_request)
    except AssetError as e:
        # Base class for all granite-assets errors
        print(e)