Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT: feat - Storage and Uploads (for Feedback) #11696

Draft
wants to merge 55 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a5414ba
reset everything
Josh-Walker-GM Sep 19, 2024
8ccdaa7
WIP
Josh-Walker-GM Sep 19, 2024
fd2650b
BROKEN WIP
Josh-Walker-GM Sep 23, 2024
2a1fcfe
Initial s3 storage adapter
dthyresson Oct 4, 2024
cdcff08
some questions on s3 needs
dthyresson Oct 4, 2024
22257d6
WIP memory adapter
dthyresson Oct 4, 2024
601b18e
First pass at upload package with plugin
dthyresson Oct 7, 2024
075001d
first pass at working upload package with web/upload
dthyresson Oct 7, 2024
779ea02
moved into web/upload
dthyresson Oct 7, 2024
045d21c
add upload setup
dthyresson Oct 8, 2024
46a1325
stub out upload and storage docs
dthyresson Oct 8, 2024
dcaadab
setup
dthyresson Oct 8, 2024
b286d7c
update upload setup after test
dthyresson Oct 8, 2024
c80dc5e
prettify
dthyresson Oct 8, 2024
c61c877
rename upload to uploads
dthyresson Oct 8, 2024
32c5e10
get to green on @redwoodjs/uploads-graphql
dthyresson Oct 8, 2024
f597bd3
get uploads package working and generalize the upload files component
dthyresson Oct 9, 2024
5dae29c
update templates for uploads
dthyresson Oct 11, 2024
250ac95
template updates
dthyresson Oct 11, 2024
9bb2162
update templates
dthyresson Oct 11, 2024
3d9323c
withStorage enun
dthyresson Oct 11, 2024
e27c546
directive updates
dthyresson Oct 12, 2024
f4a775d
add cache controls and etag
dthyresson Oct 12, 2024
7927ec5
reorg s&u docs
dthyresson Oct 12, 2024
b0c84c2
docs
dthyresson Oct 13, 2024
9726a03
docs
dthyresson Oct 13, 2024
9709e80
more docs
dthyresson Oct 13, 2024
7f87a97
uploads component
dthyresson Oct 14, 2024
6999e12
can clear accepted and rejected now
dthyresson Oct 14, 2024
d31987f
adds active message
dthyresson Oct 14, 2024
28168ca
style default renderers
dthyresson Oct 14, 2024
1eba57d
export file message utils
dthyresson Oct 14, 2024
ff72004
revoke object url
dthyresson Oct 14, 2024
4a93092
fixing upload to uploads
dthyresson Oct 14, 2024
f312aaa
exports all from react drop zone to use if roll own
dthyresson Oct 15, 2024
b8596ef
Update packages/cli/src/commands/setup/uploads/templates/api/services…
dthyresson Oct 15, 2024
96eef5b
Update packages/cli/src/commands/setup/storage/templates/withStorage.…
dthyresson Oct 15, 2024
43fac5f
better types
dthyresson Oct 15, 2024
e4b2527
refactor validateFiles
dthyresson Oct 15, 2024
34d48bc
simplify
dthyresson Oct 15, 2024
558b7cf
better types
dthyresson Oct 15, 2024
fd25c8b
rework with props
dthyresson Oct 15, 2024
02151e1
export types
dthyresson Oct 15, 2024
163fb3c
add children
dthyresson Oct 15, 2024
0b753d5
lots of component refactoring
dthyresson Oct 16, 2024
52edea8
rework previewers
dthyresson Oct 16, 2024
3025914
set base styles and message
dthyresson Oct 16, 2024
dad7d3a
allow copy paste
dthyresson Oct 16, 2024
578da4a
update web uploads readmen
dthyresson Oct 16, 2024
ebf287f
progress
dthyresson Oct 17, 2024
219b06f
use progress hook
dthyresson Oct 17, 2024
09c38da
set context and headers
dthyresson Oct 18, 2024
8621794
separate out mutation op and heder name
dthyresson Oct 18, 2024
98a398c
rework upload progress to set upload token properly
dthyresson Oct 18, 2024
158d276
remove console
dthyresson Oct 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const packages = [
// ... other packages
'upload',
// ... other packages
]
83 changes: 83 additions & 0 deletions docs/docs/storage-and-uploads/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Storage and Uploads

When you're building web applications, you'll often need to handle files. This is where **uploads** and **storage** come into play. Let's explore these concepts and see how they work together to create powerful file management systems.

Thankfully, RedwoodJS makes it easy to upload and store files with a flexible storage system that supports multiple storage backends (like AWS S3, local file system, etc).

:::tip Ready to get started?

If you want to setup and start using storage and uploads, visit the [quickstart guide](/docs/storage-and-uploads/quickstart).

Or, for more detailed guides, visit the [storage guide](/docs/storage-and-uploads/storage) and the [uploads guide](/docs/storage-and-uploads/uploads).
:::

Let's first understand how uploads and storage work.

## Understanding Uploads

Imagine you want to share a photo on a social media platform. When you click "Upload," you're initiating an upload process. Here's what happens:

1. Your device (the client) sends the file to the web server.
2. The server receives the file and decides what to do with it.

Uploads are all about getting files from the user to the server. There are several ways to implement uploads:

- Simple HTML forms with `<input type="file">`
- JavaScript methods like Ajax or the Fetch API for smoother user experiences
- More advanced approaches using GraphQL or REST APIs

## Understanding Storage

Once a file reaches the server, it needs a place to live. This is where storage comes in. Think of storage as the file's new home on the internet. There are different types of storage:

- **Local storage**: Saving files directly on the server. It's like keeping files on your computer.
- **Cloud storage**: Using services like AWS S3 or Google Cloud Storage. This is like having a huge, always-accessible hard drive in the cloud.
- **Database storage**: Storing files (usually small ones) directly in a database. This is less common but can be useful in specific scenarios.

## Storage and Uploads on Their Own

Here's where it gets interesting: uploads and storage don't always have to go hand in hand. Let's look at some examples:

### Uploads Without Storage

Imagine an online tool that converts images from one format to another:

1. You upload your image.
2. The server converts it.
3. You download the converted image.
4. The server deletes both the original and converted files.

In this case, we used the upload feature without long-term storage. This approach saves space and is great for temporary operations.

### Storage Without Uploads

Now, think about a system that generates monthly reports:

1. At the end of each month, the server creates a PDF report using data from its database.
2. The PDF is stored in the cloud.
3. Users can access this report whenever they need it.

Here, we're using storage without any user-initiated upload. The server is generating and storing files on its own.

## Storage and Uploads Together

While uploads and storage can work independently, their real power shines when they work together. Here's a common scenario:

1. A user uploads a profile picture (upload).
2. The server processes the image, perhaps resizing it (processing).
3. The processed image is saved to cloud storage (storage).
4. The server saves a link to the stored image in its database (database integration).
5. Whenever needed, the app can quickly display the user's profile picture.

This workflow combines upload, processing, storage, and database integration to create a seamless user experience.

## Why This Matters

Understanding the relationship between uploads and storage allows you to:

1. **Optimize Performance**: You can choose when to store files and when to process them on-the-fly.
2. **Enhance User Experience**: Implement features like drag-and-drop uploads or instant image previews.
3. **Scale Efficiently**: Use cloud storage to handle growing numbers of files without overloading your server.
4. **Save Resources**: Process files without storing them when long-term storage isn't necessary.

By mastering these concepts, you'll be well-equipped to handle a wide range of file management scenarios in your web applications. Whether you're building a simple photo-sharing app or a complex document management system, understanding uploads and storage will be key to your success.
18 changes: 18 additions & 0 deletions docs/docs/storage-and-uploads/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Storage and Uploads

## Quick Start

RedwoodJS makes it easy to upload and store files with a flexible storage system that supports multiple backends with just two setup commands.

```bash
yarn rw setup storage
yarn rw setup uploads
```

## Basic Example

## Common Patterns

### With Prisma Reference

### With Upload Token Validation
231 changes: 231 additions & 0 deletions docs/docs/storage-and-uploads/storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# Storage and Uploads

## Storage

RedwoodJS offers a flexible storage system that's got your back, whether you're working with local files or cloud storage like AWS S3. The best part? Switching between storage options is a breeze, thanks to a consistent API across all adapters.

### Key Features

- **Local filesystem support**: Perfect for development and simple deployments
- **S3-compatible storage**: Seamlessly integrate with popular cloud storage solutions
- **Unified API**: Write your code once, deploy anywhere without changing your storage logic
- **In-memory adapter**: Great for testing and ephemeral storage needs

With RedwoodJS storage, you can focus on building your app while we handle the nitty-gritty of file management. Let's dive in and see how easy it is to get started!

## Setup

To set up storage in your RedwoodJS app, simply run the setup command:

```bash
yarn rw setup storage
```

1.This will install the core storage package and the local filesystem adapter.

```bash
yarn add @redwoodjs/storage-core @redwoodjs/storage-adapter-filesystem
```

2. The `storage.ts` file in your `api/src/lib` directory will be updated with a default configuration using the local filesystem adapter like this:

```ts
// Setup and configuration for storage
// See: https://docs.redwoodjs.com/docs/storage

import path from 'node:path'

import { FileSystemAdapter } from '@redwoodjs/storage-adapter-filesystem'
import { StorageManager, StorageSelfSigner } from '@redwoodjs/storage-core'

const baseUrl = process.env.STORAGE_SIGNING_BASE_URL
export const signer = new StorageSelfSigner({
secret: process.env.STORAGE_SIGNING_SECRET,
})

export const storage = new StorageManager({
adapters: {
local: new FileSystemAdapter({
root: path.join(__dirname, '..', '..', '.storage'),
signing: {
signer,
baseUrl,
},
}),
special: new FileSystemAdapter({
root: path.join(__dirname, '..', '..', '.storage-special'),
signing: {
signer,
baseUrl,
},
}),
},

default: 'local',

env: {
development: 'local',
},
})
```

3. Add the following environment variables to your `.env` file:

```
STORAGE_SIGNING_BASE_URL=http://localhost:8911/storage
STORAGE_SIGNING_SECRET=super-secret
```

## Adapters

RedwoodJS storage supports multiple adapters, allowing you to switch between different storage backends easily.

### Local Filesystem

The FileSystemAdapter is perfect for local development and simple deployments. It stores files on your local filesystem.

Usage:

```ts
import { FileSystemAdapter } from '@redwoodjs/storage-adapter-filesystem'
import { StorageSelfSigner } from '@redwoodjs/storage-core'

const yourSigner = new StorageSelfSigner({
secret: process.env.STORAGE_SIGNING_SECRET,
})

const localAdapter = new FileSystemAdapter({
root: '/path/to/storage',
signing: {
signer: yourSigner,
baseUrl: 'http://your-base-url',
},
})
```

This adapter is already configured in the default `storage.ts` file.

You can customize the root directory and signing configuration as needed.

For exmaple, if you want to store files in a location called `.storage-special`, you can do so by adding a new adapter.

### AWS S3 or Tigris

For production environments, you might want to use cloud storage solutions like AWS S3 or Fly Tigris. RedwoodJS provides an S3-compatible adapter.

To use the S3 adapter:

1. Install the required package:

```bash
yarn workspace api add @redwoodjs/storage-adapter-s3
```

2. Update your `storage.ts` file to include the S3 adapter:

```ts
import { S3Adapter } from '@redwoodjs/storage-adapter-s3'

// ... existing code ...

export const storage = new StorageManager({
adapters: {
// ... existing adapters ...
s3: new S3Adapter({
bucket: process.env.AWS_BUCKET,
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
}),
},
// ... rest of the configuration ...
})
```

### The Default Adapter

### Using Different Adapters

### Environment-Specific Adapters

## Retrieving Files

To retrieve files from storage, use the readData, readFile, or readStream methods:

```ts
const buffer = await storage.readData('file-reference')
const file = await storage.readFile('file-reference')
const stream = await storage.readStream('file-reference')
```

### Temporary URLs

You can generate signed URLs for temporary access to files:

```ts
const signedUrl = await storage.getSignedUrl('file-reference')
```

## Deleting Files

To delete a file from storage:

```ts
await storage.delete('file-reference')
```

## Storing Files

To store files, use the writeData, writeFile, or writeStream methods:

```ts
await storage.writeData('file-reference', data)
await storage.writeFile('file-reference', file)
await storage.writeStream('file-reference', stream)
```

When should you use which method?

When in doubt, use `writeFile`. It's the simplest method and works in most cases.

But sometimes you might want to store binary data or you don't have a file instance yet. For that, use `writeData`.

And if you need to stream a file to storage, use `writeStream` but you'll need a stream like `fs.createReadStream` or with a File object like `File.stream()`.

## Streaming Files

You can stream files directly from storage:

```ts
const stream = await storage.stream('file-reference')
```

## Fetching Files in your Pages and Components

```graphql
type Profile {
id: String!
createdAt: DateTime!
updatedAt: DateTime!
firstName: String!
lastName: String!
avatar: String! @withStorage
}
```

### withStorage Directive

```graphql
@withStorage(format: SIGNED_URL | DATA_URI)
@withStorage(adapter: FS | S3)
@withStorage(adapter: FS | S3, format: SIGNED_URL | DATA_URI)
```

- SIGNED_URL
- DATA_URI

### Storage Function

- verifies SignedUrl
Loading