> ## Documentation Index
> Fetch the complete documentation index at: https://docs.insforge.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Referencia de SDK de almacenamiento

> Carga, descarga y gestión de archivos con el SDK de Kotlin de InsForge

## Instalación

1. Add InsForge dependencies to your project

<Tabs>
  <Tab title="Maven Central">
    build.gradle.kts:

    ```kotlin theme={null}
    repositories {
        mavenLocal() // For local development
        mavenCentral()
    }

    dependencies {
        implementation("dev.insforge:insforge-kotlin:0.1.6")
    }
    ```
  </Tab>

  <Tab title="GitHub Packages">
    First, create a GitHub Personal Access Token:

    * Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
    * Select permission: `read:packages`

    Then configure your project using one of the following methods:

    <AccordionGroup>
      <Accordion title="Option A: Environment Variables">
        settings.gradle.kts (or build.gradle.kts):

        ```kotlin theme={null}
        repositories {
            mavenCentral()
            maven {
                url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
                credentials {
                    username = System.getenv("GITHUB_USER") ?: ""
                    password = System.getenv("GITHUB_TOKEN") ?: ""
                }
            }
        }
        ```

        build.gradle.kts:

        ```kotlin theme={null}
        dependencies {
            implementation("dev.insforge:insforge-kotlin:0.1.6")
        }
        ```

        Set environment variables before building:

        ```bash theme={null}
        export GITHUB_USER="your-github-username"
        export GITHUB_TOKEN="your-personal-access-token"
        ```
      </Accordion>

      <Accordion title="Option B: gradle.properties (Local Development)">
        Add credentials to your global Gradle properties file:

        \~/.gradle/gradle.properties:

        ```properties theme={null}
        gpr.user=your-github-username
        gpr.token=ghp_xxxxxxxxxxxx
        ```

        settings.gradle.kts:

        ```kotlin theme={null}
        repositories {
            mavenCentral()
            maven {
                url = uri("https://maven.pkg.github.com/InsForge/insforge-kotlin")
                credentials {
                    username = providers.gradleProperty("gpr.user").orNull ?: ""
                    password = providers.gradleProperty("gpr.token").orNull ?: ""
                }
            }
        }
        ```

        build.gradle.kts:

        ```kotlin theme={null}
        dependencies {
            implementation("dev.insforge:insforge-kotlin:0.1.6")
        }
        ```

        <Note>
          The `~/.gradle/gradle.properties` file is stored outside your project, so credentials won't be accidentally committed to version control.
        </Note>
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

2. Initialize InsForge SDK

```kotlin theme={null}
import dev.insforge.createInsforgeClient
import dev.insforge.auth.Auth
import dev.insforge.database.Database
import dev.insforge.storage.Storage
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.ai.AI

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    install(Auth)
    install(Database)
    install(Storage)
    install(Functions)
    install(Realtime) {
        autoReconnect = true
        reconnectDelay = 5000
    }
    install(AI)
}
```

3. Enable Logging (Optional)

For debugging, you can configure the SDK log level:

```kotlin theme={null}
import dev.insforge.InsforgeLogLevel

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    // DEBUG: logs request method/URL and response status
    // VERBOSE: logs full headers and request/response bodies
    logLevel = InsforgeLogLevel.DEBUG

    install(Auth)
    install(Database)
    // ... other modules
}
```

| Log Level | Description                                       |
| --------- | ------------------------------------------------- |
| `NONE`    | No logging (default, recommended for production)  |
| `ERROR`   | Only errors                                       |
| `WARN`    | Warnings and errors                               |
| `INFO`    | Informational messages                            |
| `DEBUG`   | Debug info (request method, URL, response status) |
| `VERBOSE` | Full details (headers, request/response bodies)   |

<Note>
  Use `NONE` or `ERROR` in production to avoid exposing sensitive data in logs.
</Note>

### Android Initialization

1. Add Chrome Custom Tabs dependency to your `build.gradle.kts`:

```kotlin theme={null}
dependencies {
    implementation("androidx.browser:browser:1.9.0")
}
```

2. Initialize InsForge SDK (With Chrome Custom Tabs and Session Storage)

```kotlin theme={null}
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import dev.insforge.createInsforgeClient
import dev.insforge.ai.AI
import dev.insforge.auth.Auth
import dev.insforge.auth.BrowserLauncher
import dev.insforge.auth.ClientType
import dev.insforge.auth.SessionStorage
import dev.insforge.database.Database
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.storage.Storage

class InsforgeManager(private val context: Context) {

    val client = createInsforgeClient(
        baseUrl = "https://your-app.insforge.app",
        anonKey = "your-anon-key"
    ) {
        install(Auth) {
            // 1. Chrome Custom Tabs - opens in-app, similar to iOS ASWebAuthenticationSession
            browserLauncher = BrowserLauncher { url ->
                val customTabsIntent = CustomTabsIntent.Builder()
                    .setShowTitle(true)
                    .build()
                // Handle non-Activity context safely
                if (context is Activity) {
                    customTabsIntent.launchUrl(context, Uri.parse(url))
                } else {
                    customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    customTabsIntent.launchUrl(context, Uri.parse(url))
                }
            }

            // 2. enable session persistence
            persistSession = true

            // 3. config SessionStorage (use SharedPreferences)
            sessionStorage = object : SessionStorage {
                private val prefs = context.getSharedPreferences(
                    "insforge_auth",
                    Context.MODE_PRIVATE
                )

                override suspend fun save(key: String, value: String) {
                    prefs.edit().putString(key, value).apply()
                }

                override suspend fun get(key: String): String? {
                    return prefs.getString(key, null)
                }

                override suspend fun remove(key: String) {
                    prefs.edit().remove(key).apply()
                }
            }

            // 4. set client type for mobile
            clientType = ClientType.MOBILE
        }
        // Install Database module
        install(Database)

        // Install Realtime module for real-time subscriptions
        install(Realtime) {
            debug = true
        }
        // Install other modules
        install(Storage)
        install(Functions)
        install(AI)
    }
}
```

3. Use Jetpack DataStore for Session Storage (Optional)

```kotlin theme={null}
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

val Context.authDataStore: DataStore<Preferences> by preferencesDataStore(name = "insforge_auth")

class DataStoreSessionStorage(private val context: Context) : SessionStorage {
    
    override suspend fun save(key: String, value: String) {
        context.authDataStore.edit { prefs ->
            prefs[stringPreferencesKey(key)] = value
        }
    }
    
    override suspend fun get(key: String): String? {
        return context.authDataStore.data.map { prefs ->
            prefs[stringPreferencesKey(key)]
        }.first()
    }
    
    override suspend fun remove(key: String) {
        context.authDataStore.edit { prefs ->
            prefs.remove(stringPreferencesKey(key))
        }
    }
}

// Then use it in your InsForge client
install(Auth) {
    browserLauncher = ...
    persistSession = true
    sessionStorage = DataStoreSessionStorage(context)
}
```

## from()

Obtener una instancia de depósito para operaciones de archivos.

### Ejemplo

```kotlin theme={null}
val bucket = insforge.storage.from("images")
```

***

## upload()

Cargar un archivo con una ruta/clave específica.

### Parámetros

* `path` (String) - Ruta/clave del archivo en el depósito
* `data` (ByteArray) - Contenido del archivo como bytes
* `options` (DSL block, optional) - Opciones de carga

### UploadOptions

| Opción        | Tipo                     | Descripción                                               |
| ------------- | ------------------------ | --------------------------------------------------------- |
| `contentType` | String?                  | Tipo MIME (por ejemplo, "image/jpeg")                     |
| `upsert`      | Boolean                  | Sobrescribir si el archivo existe (predeterminado: false) |
| `metadata`    | `Map\<String, String\>?` | Metadatos personalizados                                  |

### Ejemplo

```kotlin theme={null}
// Basic upload
val imageData = bitmap.toByteArray()
val result = insforge.storage
    .from("images")
    .upload("posts/post-123/cover.jpg", imageData)

Log.d("Storage", "Uploaded: ${result.url}")

// Upload with options
val result = insforge.storage
    .from("images")
    .upload("posts/post-123/cover.jpg", imageData) {
        contentType = "image/jpeg"
        upsert = true  // Overwrite if exists
        metadata = mapOf("userId" to "123")
    }

// Using bracket syntax
val result = insforge.storage["images"]
    .upload("avatars/user-123.jpg", imageData) {
        contentType = "image/jpeg"
    }
```

### Cargar desde Android Uri

Los desarrolladores pueden usar la siguiente función para cargar archivos desde una Uri de Android:

```kotlin theme={null}
// Android Uri Upload sample:
suspend fun uploadFromUri(
    bucket: BucketApi,
    uri: Uri,
    context: Context,
    path: String
): FileUploadResponse {
    val data = context.contentResolver.openInputStream(uri)?.use {
        it.readBytes()
    } ?: throw IllegalArgumentException("Cannot read Uri")

    val contentType = context.contentResolver.getType(uri)

    return bucket.upload(path, data) {
        this.contentType = contentType
    }
}
```

***

## uploadWithAutoKey()

Cargar un archivo con una clave única generada automáticamente.

### Parámetros

* `filename` (String) - Nombre de archivo original (utilizado para detección de extensión)
* `data` (ByteArray) - Contenido del archivo como bytes
* `options` (DSL block, optional) - Opciones de carga

### Ejemplo

```kotlin theme={null}
val imageData = bitmap.toByteArray()
val result = insforge.storage
    .from("uploads")
    .uploadWithAutoKey("photo.jpg", imageData) {
        contentType = "image/jpeg"
    }

Log.d("Storage", "Auto-generated key: ${result.key}")
Log.d("Storage", "URL: ${result.url}")

// Save to database
insforge.database
    .from("posts")
    .insertTyped(listOf(
        Post(
            imageUrl = result.url,
            imageKey = result.key,
            userId = userId
        )
    ))
    .returning()
    .execute<Post>()
```

***

## download()

Descargar un archivo como ByteArray.

### Ejemplo

```kotlin theme={null}
// Download file
val data = insforge.storage
    .from("images")
    .download(path = "posts/post-123/cover.jpg")

// Convert to Bitmap
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
imageView.setImageBitmap(bitmap)
```

***

## delete()

Eliminar un archivo del almacenamiento.

### Ejemplo

```kotlin theme={null}
// Get the file key from database
val posts = insforge.database
    .from("posts")
    .select("image_key")
    .eq("id", "post-123")
    .execute<Post>()  // Returns List<Post>

val post = posts.firstOrNull() ?: return

// Delete from storage
insforge.storage
    .from("images")
    .delete(post.imageKey)

// Clear database reference
insforge.database
    .from("posts")
    .update(buildJsonObject {
        put("image_url", JsonNull)
        put("image_key", JsonNull)
    })
    .eq("id", "post-123")
    .execute<Post>()
```

***

## createSignedUrl()

Crear una URL firmada para acceso temporal a un archivo.

### Parámetros

* `path` (String) - Ruta del archivo en el depósito
* `expiresIn` (Int) - Tiempo de caducidad en segundos

### Ejemplo

```kotlin theme={null}
// Create a signed URL (expires in 1 hour)
val url = insforge.storage
    .from("images")
    .createSignedUrl("posts/post-123/cover.jpg", expiresIn = 3600)

Log.d("Storage", "Signed URL: $url")
```

***

## getDownloadUrl()

Obtener una URL de descarga para un archivo.

### Ejemplo

```kotlin theme={null}
val strategy = insforge.storage
    .from("images")
    .getDownloadUrl("posts/post-123/cover.jpg")

Log.d("Storage", "Download URL: ${strategy.url}")
```

***

## list()

Listar archivos en un depósito con filtrado opcional.

### Opciones de BucketListFilter

| Opción      | Tipo      | Descripción                                      |
| ----------- | --------- | ------------------------------------------------ |
| `prefix`    | String?   | Filtrar por prefijo de ruta                      |
| `limit`     | Int       | Resultados máximos (predeterminado: 100)         |
| `offset`    | Int       | Desplazamiento de paginación (predeterminado: 0) |
| `sortBy`    | String?   | Campo de ordenamiento                            |
| `sortOrder` | SortOrder | ASC o DESC (predeterminado: ASC)                 |

### Ejemplo

```kotlin theme={null}
// List all files in bucket
val files = insforge.storage
    .from("images")
    .list()

files.forEach { file ->
    Log.d("Storage", "File: ${file.name}, Size: ${file.metadata?.size}")
}

// List with filters
val files = insforge.storage
    .from("images")
    .list {
        prefix = "posts/"
        limit = 50
        offset = 0
        sortBy = "created_at"
        sortOrder = SortOrder.DESC
    }
```

***

## Gestión de depósitos

### listBuckets()

Listar todos los depósitos de almacenamiento.

```kotlin theme={null}
val buckets = insforge.storage.listBuckets()

buckets.forEach { bucket ->
    Log.d("Storage", "Bucket: ${bucket.id}, Public: ${bucket.isPublic}")
}
```

### createBucket()

Crear un nuevo depósito de almacenamiento.

```kotlin theme={null}
val bucket = insforge.storage.createBucket("my-bucket") {
    isPublic = true
}

Log.d("Storage", "Created bucket: ${bucket.id}")
```

### updateBucket()

Actualizar la configuración del depósito.

```kotlin theme={null}
insforge.storage.updateBucket("my-bucket") {
    isPublic = false
}
```

### deleteBucket()

Eliminar un depósito de almacenamiento.

```kotlin theme={null}
insforge.storage.deleteBucket("my-bucket")
```

***

## Carga avanzada

### getUploadStrategy()

Obtener estrategia de carga para archivos grandes o cargas reanudables.

```kotlin theme={null}
val strategy = insforge.storage
    .from("videos")
    .getUploadStrategy("videos/large-file.mp4", fileSize = 100_000_000)

// Use strategy.url for upload
Log.d("Storage", "Upload URL: ${strategy.url}")
```

### confirmUpload()

Confirmar una carga completada.

```kotlin theme={null}
insforge.storage
    .from("videos")
    .confirmUpload("videos/large-file.mp4")
```
