# Overview

> Learn how to use images and file storage in the starter kit.

Nuxt Starter Kit uses [Nuxt Image](https://nuxt.com/modules/image) module to resize and transform your images using the TwicPics CDN.

## Image Provider

The starter kit uses **TwicPics** as the image provider for CDN delivery. TwicPics provides:

- Automatic image optimization
- Responsive images
- Format conversion (WebP, AVIF)
- Lazy loading support

## Configuration

Images are configured in `apps/web/nuxt.config.ts`:

```typescript
export default defineNuxtConfig({
  image: {
    provider: 'twicpics',
    twicpics: {
      baseURL: siteConfig.imageProviderUrl
    }
  }
})
```

The `imageProviderUrl` is defined in `packages/site-config/siteConfig.ts`.

## Usage

Use the `<NuxtImg>` component for optimized images:

```vue
<template>
  <NuxtImg
    src="landing-page/hero.png"
    alt="Hero image"
    width="1200"
    height="600"
    loading="lazy"
  />
</template>
```

## Static Images

Place static images in the `public/` directory:

```text
public/
  ├── landing-page/
  │   ├── hero.png
  │   └── features.png
  └── docs/
      └── screenshots/
```

## Image Optimization Tips

1. **Use appropriate formats**: WebP for web, AVIF for modern browsers
2. **Lazy loading**: Add `loading="lazy"` for below-the-fold images
3. **Responsive images**: Use `sizes` and `srcset` attributes
4. **Compress before upload**: Pre-compress large images before adding to repository

## File Storage

The starter kit includes file storage functionality through the `layer-storage` package with AWS S3 integration for handling user uploads.

### AWS S3 Configuration

Set up your AWS S3 credentials in your `.env` file:

```bash
NUXT_PRIVATE_AWS_S3_ACCESS_KEY_ID=your-access-key-id
NUXT_PRIVATE_AWS_S3_SECRET_ACCESS_KEY=your-secret-access-key
NUXT_PRIVATE_AWS_S3_BUCKET_NAME=your-bucket-name
NUXT_PRIVATE_AWS_S3_REGION=us-east-1
```

### AWS S3 Setup

1. **Create an S3 Bucket**:
  - Go to [AWS S3 Console](https://console.aws.amazon.com/s3/)
  - Create a new bucket with a unique name
  - Configure bucket settings (versioning, encryption, etc.)
2. **Configure CORS** (if accessing files from browser):```json
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
    "AllowedOrigins": ["https://your-domain.com"],
    "ExposeHeaders": []
  }
]
```
3. **Create IAM User**:
  - Go to IAM Console
  - Create a new user with programmatic access
  - Attach policy with S3 permissions (or use `AmazonS3FullAccess` for development)
  - Save the Access Key ID and Secret Access Key

### Recommended IAM Policy

For production, use a more restrictive policy:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::your-bucket-name/*",
        "arn:aws:s3:::your-bucket-name"
      ]
    }
  ]
}
```

### Storage Layer

The storage functionality is located in `packages/layer-storage`:

- **Server utilities**: Upload, delete, and manage files
- **Database schema**: File metadata storage in `server/db/schema/file.ts`
- **Client composables**: File upload utilities

Files are tracked in the database with metadata including file name, path, size, content type, upload timestamp, and associated user ID.

### Uploading Files

**Server-side:**

```typescript
// server/api/upload.post.ts
import { uploadFileToS3 } from 'layer-storage/server/utils/storage'

export default defineEventHandler(async (event) => {
  const { user } = await requireAuth(event)
  const formData = await readFormData(event)
  const file = formData.get('file') as File

  const result = await uploadFileToS3(file, {
    folder: 'uploads',
    userId: user.id
  })

  return result
})
```

**Client-side:**

```vue
<script setup lang="ts">
const file = ref<File | null>(null)
const uploading = ref(false)

async function handleUpload () {
  if (!file.value)
    return

  uploading.value = true
  const formData = new FormData()
  formData.append('file', file.value)

  try {
    await $fetch('/api/upload', {
      body: formData,
      method: 'POST'
    })
    // Handle success
  }
  catch (error) {
    // Handle error
  }
  finally {
    uploading.value = false
  }
}
</script>

<template>
  <div>
    <input
      type="file"
      @change="file = $event.target.files[0]"
    >
    <button
      :disabled="uploading"
      @click="handleUpload"
    >
      {{ uploading ? 'Uploading...' : 'Upload' }}
    </button>
  </div>
</template>
```

### File Access

Files stored in S3 can be accessed via:

1. **Direct S3 URLs** - For public files
2. **Signed URLs** - For private files with temporary access
3. **CloudFront CDN** - For optimized delivery (recommended)

Generate signed URLs for private files:

```typescript
import { getSignedUrl } from 'layer-storage/server/utils/storage'

const signedUrl = await getSignedUrl(fileKey, {
  expiresIn: 3600 // 1 hour
})
```

### Storage Best Practices

1. **Validate file types** - Only allow expected file types
2. **Limit file sizes** - Set appropriate size limits
3. **Scan for malware** - Implement virus scanning for user uploads
4. **Use signed URLs** - For private files, always use signed URLs
5. **Organize files** - Use consistent folder structures (e.g., `uploads/{userId}/{filename}`)
6. **Set lifecycle policies** - Automatically delete or archive old files
7. **Enable versioning** - Protect against accidental deletions
8. **Monitor costs** - Set up CloudWatch alerts for S3 usage

### Alternative Storage Providers

While the starter kit uses AWS S3 by default, you can integrate other S3-compatible storage providers:

- **Cloudflare R2** - S3-compatible with no egress fees
- **DigitalOcean Spaces** - S3-compatible object storage
- **Backblaze B2** - Cost-effective S3-compatible storage
- **Google Cloud Storage** - Google's object storage solution

To use an alternative provider, update the storage utilities in `packages/layer-storage/server/utils/storage.ts` with the provider's SDK and configuration.
