Python FastAPI + StorageVault (S3 objektisalvestus)

    Selles juhendis loome Python FastAPI rakenduse, mis kasutab Pilvio StorageVault'i (S3-ühilduv objektisalvestus) failide üles- ja allalaadimiseks.

    Mida ehitame

    • FastAPI REST API koos failide üleslaadimisega
    • S3-ühilduv ühendus Pilvio StorageVault'iga
    • Eelsigneeritud URL-id turvaliseks failide jagamiseks

    Eeldused

    • Pilvio konto ja API token (vaata ülevaadet)
    • Python 3.11+
    • Pilvio S3 võtmed (access key + secret key)

    1. samm: StorageVault (S3) ettevalmistamine

    S3 võtmete hankimine

    # S3 kasutajainfo ja võtmete pärimine
    curl "https://api.pilvio.com/v1/storage/user" \
      -H "apikey: SINU_PILVIO_TOKEN" \
      -X GET
    

    Vastuses leiad s3Credentials massiivis oma accessKey ja secretKey väärtused. Kui võtmeid pole, genereeri uus paar:

    curl "https://api.pilvio.com/v1/storage/user/keys" \
      -H "apikey: SINU_PILVIO_TOKEN" \
      -X POST
    

    Bucketi loomine

    curl "https://api.pilvio.com/v1/storage/bucket" \
      -H "apikey: SINU_PILVIO_TOKEN" \
      -X PUT \
      -d "name=minu-rakendus-failid" \
      -d "billing_account_id=SINU_BILLING_ID"
    

    Märkus: Bucketi nimi peab olema globaalselt unikaalne kõigi Pilvio kasutajate seas.

    2. samm: VM loomine ja ettevalmistamine

    # VM loomine
    curl "https://api.pilvio.com/v1/user-resource/vm" \
      -H "apikey: SINU_PILVIO_TOKEN" \
      -X POST \
      -d "name=fastapi-server" \
      -d "os_name=ubuntu" \
      -d "os_version=24.04" \
      -d "vcpu=2" \
      -d "ram=2048" \
      -d "disks=20" \
      -d "username=deploy" \
      -d "password=TurvalineParool123!" \
      -d "public_key=ssh-ed25519 AAAA... sinu@arvuti"
    

    Pärast VM-i loomist ja Floating IP sidumist (vaata Node.js juhendit):

    ssh deploy@SINU_FLOATING_IP
    
    # Python ja pip paigaldamine
    sudo apt-get update
    sudo apt-get install -y python3-pip python3-venv
    
    # Projekti ettevalmistamine
    mkdir -p ~/fastapi-app && cd ~/fastapi-app
    python3 -m venv venv
    source venv/bin/activate
    

    3. samm: FastAPI rakenduse loomine

    pip install fastapi uvicorn python-multipart boto3 python-dotenv
    

    Loo fail main.py:

    import os
    from datetime import datetime
    from contextlib import asynccontextmanager
    
    import boto3
    from botocore.config import Config
    from dotenv import load_dotenv
    from fastapi import FastAPI, UploadFile, HTTPException
    from fastapi.middleware.cors import CORSMiddleware
    
    load_dotenv()
    
    # Pilvio StorageVault (S3) klient
    s3_client = boto3.client(
        "s3",
        endpoint_url="https://s3.pilvio.com:8080",
        aws_access_key_id=os.getenv("PILVIO_S3_ACCESS_KEY"),
        aws_secret_access_key=os.getenv("PILVIO_S3_SECRET_KEY"),
        config=Config(signature_version="s3v4"),
        region_name="eu-east-1",  # Pilvio ei nõua regiooni, aga boto3 vajab
    )
    
    BUCKET_NAME = os.getenv("PILVIO_S3_BUCKET", "minu-rakendus-failid")
    
    app = FastAPI(title="Pilvio FastAPI + StorageVault")
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    
    @app.get("/health")
    async def health():
        return {"status": "ok", "timestamp": datetime.now().isoformat()}
    
    
    @app.post("/api/v1/files/upload")
    async def upload_file(file: UploadFile):
        """Laadi fail üles Pilvio StorageVault'i."""
        if not file.filename:
            raise HTTPException(400, "Failinimi puudub")
    
        # Genereeri unikaalne võti
        timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
        key = f"uploads/{timestamp}-{file.filename}"
    
        try:
            s3_client.upload_fileobj(
                file.file,
                BUCKET_NAME,
                key,
                ExtraArgs={"ContentType": file.content_type or "application/octet-stream"},
            )
        except Exception as e:
            raise HTTPException(500, f"Üleslaadimine ebaõnnestus: {str(e)}")
    
        return {"key": key, "bucket": BUCKET_NAME, "size": file.size}
    
    
    @app.get("/api/v1/files")
    async def list_files(prefix: str = "uploads/"):
        """Näita faile StorageVault bucketis."""
        try:
            response = s3_client.list_objects_v2(Bucket=BUCKET_NAME, Prefix=prefix)
            files = [
                {
                    "key": obj["Key"],
                    "size": obj["Size"],
                    "modified": obj["LastModified"].isoformat(),
                }
                for obj in response.get("Contents", [])
            ]
            return {"files": files, "count": len(files)}
        except Exception as e:
            raise HTTPException(500, f"Failide loetelu ebaõnnestus: {str(e)}")
    
    
    @app.get("/api/v1/files/download-url/{file_key:path}")
    async def get_download_url(file_key: str, expires_in: int = 3600):
        """Genereeri eelsigneeritud allalaadimis-URL (vaikimisi kehtib 1 tund)."""
        try:
            url = s3_client.generate_presigned_url(
                "get_object",
                Params={"Bucket": BUCKET_NAME, "Key": file_key},
                ExpiresIn=expires_in,
            )
            return {"url": url, "expires_in": expires_in}
        except Exception as e:
            raise HTTPException(500, f"URL-i genereerimine ebaõnnestus: {str(e)}")
    
    
    @app.delete("/api/v1/files/{file_key:path}")
    async def delete_file(file_key: str):
        """Kustuta fail StorageVault'ist."""
        try:
            s3_client.delete_object(Bucket=BUCKET_NAME, Key=file_key)
            return {"deleted": file_key}
        except Exception as e:
            raise HTTPException(500, f"Kustutamine ebaõnnestus: {str(e)}")
    

    Loo fail .env:

    PILVIO_S3_ACCESS_KEY=sinu-access-key
    PILVIO_S3_SECRET_KEY=sinu-secret-key
    PILVIO_S3_BUCKET=minu-rakendus-failid
    ALLOWED_ORIGINS=https://sinu-domeen.ee
    

    4. samm: Käivitamine

    Arendusel

    uvicorn main:app --reload --host 0.0.0.0 --port 8000
    

    Tootmises (systemd)

    Loo fail /etc/systemd/system/fastapi.service:

    [Unit]
    Description=FastAPI + StorageVault
    After=network.target
    
    [Service]
    User=deploy
    WorkingDirectory=/home/deploy/fastapi-app
    Environment="PATH=/home/deploy/fastapi-app/venv/bin"
    EnvironmentFile=/home/deploy/fastapi-app/.env
    ExecStart=/home/deploy/fastapi-app/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 2
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    
    sudo systemctl daemon-reload
    sudo systemctl enable --now fastapi
    

    5. samm: Testimine

    # Health check
    curl http://SINU_FLOATING_IP:8000/health
    
    # Faili üleslaadimine
    curl -X POST http://SINU_FLOATING_IP:8000/api/v1/files/upload \
      -F "file=@dokument.pdf"
    
    # Failide loetelu
    curl http://SINU_FLOATING_IP:8000/api/v1/files
    
    # Allalaadimis-URL
    curl http://SINU_FLOATING_IP:8000/api/v1/files/download-url/uploads/20250211-120000-dokument.pdf
    

    Otse S3 API kasutamine (boto3 klient või AWS CLI)

    Pilvio StorageVault on S3-ühilduv, seega toimib ka AWS CLI:

    # Seadista AWS CLI Pilvio jaoks
    aws configure set aws_access_key_id SINU_ACCESS_KEY
    aws configure set aws_secret_access_key SINU_SECRET_KEY
    
    # Failide loetelu
    aws s3 ls s3://minu-rakendus-failid/ --endpoint-url https://s3.pilvio.com:8080
    
    # Faili üleslaadimine
    aws s3 cp fail.txt s3://minu-rakendus-failid/ --endpoint-url https://s3.pilvio.com:8080
    

    StorageVault API haldamine

    # Bucketi info
    curl "https://api.pilvio.com/v1/storage/bucket?name=minu-rakendus-failid" \
      -H "apikey: SINU_PILVIO_TOKEN"
    
    # Kõik bucketid
    curl "https://api.pilvio.com/v1/storage/bucket/list" \
      -H "apikey: SINU_PILVIO_TOKEN"
    
    # S3 võtmete haldamine
    curl "https://api.pilvio.com/v1/storage/user/keys" \
      -H "apikey: SINU_PILVIO_TOKEN"
    

    Järgmised sammud: Kombineeri StorageVault failisalvestus React SPA frontendiga või lisa PostgreSQL andmebaas metaandmete jaoks.