Skip to content

Commit

Permalink
Merge branch 'main' into sveltekit-docs
Browse files Browse the repository at this point in the history
* main:
  Remix docs (#288)
  add facebook for real (#301)
  enable facebook (#300)
  Fix "Edit this page" link (#299)
  deps: update uppy to 4.9.0 (#297)
  Add @uppy/webdav docs (#296)
  fix docs for picker (#295)
  fix app id (#294)
  Add new Google Picker plugins to examples (#293)
  Also disable Instagram/Google Drive on the homepage
  Disable Instagram/Facebook on examples for now
  add dynamic year and range (#292)
  deps: update uppy to 4.8.0 (#291)
  update doc (#280)
  Improve companionAllowedHosts doc (#290)
  Document @uppy/google-photos-picker and @uppy/google-drive-picker (#281)
  • Loading branch information
Murderlon committed Jan 8, 2025
2 parents dae489f + 39e8083 commit 29a8f83
Show file tree
Hide file tree
Showing 25 changed files with 1,246 additions and 832 deletions.
19 changes: 15 additions & 4 deletions docs/companion.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ OAuth.
## When should I use it?

If you want to let users download files from [Box][], [Dropbox][], [Facebook][],
[Google Drive][googledrive], [Google Photos][googlephotos], [Instagram][],
[OneDrive][], [Unsplash][], [Import from URL][url], or [Zoom][] — you need
Companion.
[Google Drive][googledrive], [Google Photos][googlephotos], [Google Drive
Picker][googledrivepicker], [Google Photos Picker][googlephotospicker],
[Instagram][], [OneDrive][], [Unsplash][], [Import from URL][url], or [Zoom][]
you need Companion.

Companion supports the same [uploaders](/docs/guides/choosing-uploader) as Uppy:
[Tus](/docs/tus), [AWS S3](/docs/aws-s3), and [regular multipart](/docs/tus).
Expand Down Expand Up @@ -638,7 +639,10 @@ Prometheus metrics (by default metrics are enabled.)
A boolean flag to tell Companion whether to enable streaming uploads. If
enabled, it will lead to _faster uploads_ because companion will start uploading
at the same time as downloading using `stream.pipe`. If `false`, files will be
fully downloaded first, then uploaded. Defaults to `true`.
fully downloaded first, then uploaded. Note that for Tus to suppport streaming
files of unknown size, this requires an optional extension on the Tus server if
using Tus uploads. For form multipart uploads it requres a server that can
handle `transfer-encoding: chunked`. Defaults to `true`.

#### `maxFileSize` `COMPANION_MAX_FILE_SIZE`

Expand Down Expand Up @@ -721,6 +725,11 @@ as well as
Set this to `true` to enable the [URL functionalily](https://uppy.io/docs/url/).
Default: `false`.

#### `enableGooglePickerEndpoint` `COMPANION_ENABLE_GOOGLE_PICKER_ENDPOINT`

Set this to `true` to enable the Google Picker (Photos / Drive) functionality.
Default: `false`.

### Events

The object returned by `companion.app()` also has a property `companionEmitter`
Expand Down Expand Up @@ -981,6 +990,8 @@ automatically restart when files are changed.
[facebook]: /docs/facebook
[googledrive]: /docs/google-drive
[googlephotos]: /docs/google-photos
[googledrivepicker]: /docs/google-drive-picker
[googlephotospicker]: /docs/google-photos-picker
[instagram]: /docs/instagram
[onedrive]: /docs/onedrive
[unsplash]: /docs/unsplash
Expand Down
291 changes: 291 additions & 0 deletions docs/framework-integrations/remix.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
# Remix

Integration guide for the [React][] components for the Uppy UI plugins and
hooks.

:::tip

Uppy also has hooks and more React examples in the [React docs](/docs/react).

:::

## Install

```shell
npm install @uppy/core @uppy/dashboard @uppy/react
```

## Tus

[Tus][tus] is an open protocol for resumable uploads built on HTTP. This means
accidentally closing your tab or losing connection lets you continue, for
instance, your 10GB upload instead of starting all over.

Tus supports any language, any platform, and any network. It requires a client
and server integration to work. We will be using [tus Node.js][].

Checkout the [`@uppy/tus` docs](/docs/tus) for more information.

Create a route where you want to handle uploads:

```tsx
import { useEffect, useState } from 'react';
import Uppy from '@uppy/core';
import Dashboard from '@uppy/react/lib/Dashboard';
import Tus from '@uppy/tus';

import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';

function createUppy() {
return new Uppy().use(Tus, { endpoint: '/api/upload' });
}

export default function UppyDashboard() {
// Important: use an initializer function to prevent the state from recreating.
const [uppy] = useState(createUppy);

return <Dashboard theme="dark" uppy={uppy} />;
}
```

[`@tus/server`][tus Node.js] does not support remix resource routes yet, which
is based on the fetch `Request` API instead of `http.IncomingMessage` and
`http.ServerResponse`.

However we can workaround it by using a custom server, which is a common pattern
with Remix. Remix supports multiple
[adapters](https://remix.run/docs/en/main/other-api/adapter), choose the one
that fits your needs.

The exact code to integrate [tus Node.js][] in your custom server depends on the
framework you choose. Head over to the `@tus/server` [examples for the most popular Node.js servers](https://github.com/tus/tus-node-server/tree/main/packages/server#examples)
to find the one that works for you.

## Transloadit

:::note

Before continuing you should have a [Transloadit](https://transloadit.com)
account and a
[template](https://transloadit.com/docs/getting-started/my-first-app/) setup.

:::

Transloadit’s strength is versatility. By doing video, audio, images, documents,
and more, you only need one vendor for [all your file processing
needs][transloadit-services]. The [`@uppy/transloadit`](/docs/transloadit)
plugin directly uploads to Transloadit so you only have to worry about creating
a [template][transloadit-concepts]. It uses [Tus](#tus) under the hood so you
don’t have to sacrifice reliable, resumable uploads for convenience.

When you go to production always make sure to set the `signature`. **Not using
[Signature Authentication](https://transloadit.com/docs/topics/signature-authentication/)
can be a security risk**. Signature Authentication is a security measure that
can prevent outsiders from tampering with your Assembly Instructions.

Generating a signature should be done on the server to avoid leaking secrets.

Start by creating a
[resource route](https://remix.run/docs/en/main/guides/resource-routes) to
generate the signature and params:

```ts
import { json } from '@remix-run/node';
import type { ActionFunction } from '@remix-run/node';
import crypto from 'crypto';

function utcDateString(ms: number): string {
return new Date(ms)
.toISOString()
.replace(/-/g, '/')
.replace(/T/, ' ')
.replace(/\.\d+Z$/, '+00:00');
}

export const action: ActionFunction = async ({ request }) => {
// expire 1 hour from now (this must be milliseconds)
const expires = utcDateString(Date.now() + 1 * 60 * 60 * 1000);
const authKey = process.env.TRANSLOADIT_KEY;
const authSecret = process.env.TRANSLOADIT_SECRET;
const templateId = process.env.TRANSLOADIT_TEMPLATE_ID;

if (!authKey || !authSecret || !templateId) {
throw json({ error: 'Missing Transloadit credentials' }, { status: 500 });
}

const body = await request.json();
const params = JSON.stringify({
auth: {
key: authKey,
expires,
},
template_id: templateId,
fields: {
// You can use this in your template.
customValue: body.customValue,
},
// your other params like notify_url, etc.
});

const signatureBytes = crypto
.createHmac('sha384', authSecret)
.update(Buffer.from(params, 'utf-8'));
// The final signature needs the hash name in front, so
// the hashing algorithm can be updated in a backwards-compatible
// way when old algorithms become insecure.
const signature = `sha384:${signatureBytes.digest('hex')}`;

return json({ expires, signature, params });
};
```

On the client we want to fetch the signature and params from the server. You may
want to send values from React state along to your endpoint, for instance to add
[`fields`](https://transloadit.com/docs/topics/assembly-variables/) which you
can use in your template as global variables.

```js
// app/routes/upload-transloadit.tsx
import { Transloadit } from '@uppy/transloadit';

function createUppy() {
const uppy = new Uppy();
uppy.use(Transloadit, {
async assemblyOptions() {
// You can send meta data along for use in your template.
// https://transloadit.com/docs/topics/assembly-instructions/#form-fields-in-instructions
const { meta } = uppy.getState();
const body = JSON.stringify({ customValue: meta.customValue });
const res = await fetch('/transloadit-params', {
method: 'POST',
body,
});
return res.json();
},
});
return uppy;
}

function Component({ customValue }) {
// IMPORTANT: passing an initializer function to prevent the state from recreating.
const [uppy] = useState(createUppy);

useEffect(() => {
if (customValue) {
uppy.setOptions({ meta: { customValue } });
}
}, [uppy, customValue]);
}
```

## HTTP uploads to your backend

If you want to handle uploads yourself, you can use
[`@uppy/xhr-upload`](/docs/xhr-upload).

Create a
[resource route](https://remix.run/docs/en/main/guides/resource-routes), such as
`app/routes/upload/route.ts`:

```ts
import { LocalFileStorage } from '@mjackson/file-storage/local';
import { type FileUpload, parseFormData } from '@mjackson/form-data-parser';
import { type ActionFunctionArgs } from '@remix-run/node';

const fileStorage = new LocalFileStorage('./uploads');

export async function action({ request }: ActionFunctionArgs) {
const user = requireUser(request); // your optional logic
const uploadHandler = async (fileUpload: FileUpload) => {
const storageKey = `user-${user.id}-avatar`;
await fileStorage.set(storageKey, fileUpload);
return fileStorage.get(storageKey);
};
const formData = await parseFormData(request, uploadHandler);
for (const [key, value] of formData.entries()) {
console.log(key, value);
}
return Response.json(null);
}
```

Create a page with your Uppy instance:

```tsx
import Uppy from '@uppy/core';
import Dashboard from '@uppy/react/lib/Dashboard';
import Xhr from '@uppy/xhr-upload';
import { useState } from 'react';

import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';

function createUppy() {
return new Uppy().use(Xhr, { endpoint: '/upload' });
}

export default function UppyDashboard() {
// Important: use an initializer function to prevent the state from recreating.
const [uppy] = useState(createUppy);

return <Dashboard uppy={uppy} />;
}
```

If you want to send along other form fields with your upload, you can use
[`@uppy/form`](/docs/form).

```tsx {4,20-23,26-30}
import Uppy from '@uppy/core';
import Dashboard from '@uppy/react/lib/Dashboard';
import Xhr from '@uppy/xhr-upload';
import Form from '@uppy/form';
import { useEffect, useState } from 'react';

import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';

function createUppy() {
return new Uppy().use(Xhr, { endpoint: '/upload' });
}

export default function UppyDashboard() {
// Important: use an initializer function to prevent the state from recreating.
const [uppy] = useState(createUppy);

// @uppy/form is client-side only so we need to install it
// when the component is mounted.
useEffect(() => {
uppy.use(Form, { target: '#form' });
return () => uppy.removePlugin(uppy.getPlugin('Form')!);
}, [uppy]);

return (
<form id="form">
<label htmlFor="dob">Date of birth: </label>
<input type="date" name="dob" />
<Dashboard uppy={uppy} />;
</form>
);
}
```

## Next steps

- Add client-side file [restrictions](/docs/uppy/#restrictions).
- Upload files together with other form fields with [`@uppy/form`](/docs/form).
- Use your [language of choice](/docs/locales) instead of English.
- Add an [image editor](docs/image-editor) for cropping and resizing images.
- Download files from remote sources, such as [Google Drive](docs/google-drive)
and [Dropbox](docs/dropbox), with [Companion](/docs/companion).
- Add [Golden Retriever](/docs/golden-retriever) to save selected files in your
browser cache, so that if the browser crashes, or the user accidentally closes
the tab, Uppy can restore everything and continue uploading as if nothing
happened.

[transloadit-concepts]: https://transloadit.com/docs/getting-started/concepts/
[transloadit-services]: https://transloadit.com/services/
[react]: https://facebook.github.io/react
[tus]: https://tus.io/
[tus Node.js]: https://github.com/tus/tus-node-server
Loading

0 comments on commit 29a8f83

Please sign in to comment.