Support for additional S3 providers (#71)

* support for other s3 providers

* remove redundant route options

* use type safe env

* prettier
This commit is contained in:
Mert Demir
2024-02-01 07:00:19 +09:00
committed by GitHub
parent e6467b41fc
commit 08d75fd75c
5 changed files with 35 additions and 10 deletions

View File

@@ -74,6 +74,12 @@ S3_UPLOAD_BUCKET=name-of-s3-bucket
S3_UPLOAD_REGION=us-east-1 S3_UPLOAD_REGION=us-east-1
``` ```
You can also use other S3 providers by providing a custom endpoint:
```.env
S3_UPLOAD_ENDPOINT=http://localhost:9000
```
### Create expense from receipt ### Create expense from receipt
You can offer users to create expense by uploading a receipt. This feature relies on [OpenAI GPT-4 with Vision](https://platform.openai.com/docs/guides/vision). You can offer users to create expense by uploading a receipt. This feature relies on [OpenAI GPT-4 with Vision](https://platform.openai.com/docs/guides/vision).

View File

@@ -1,14 +1,27 @@
/**
* Undefined entries are not supported. Push optional patterns to this array only if defined.
* @type {import('next/dist/shared/lib/image-config').RemotePattern}
*/
const remotePatterns = []
// S3 Storage
if (process.env.S3_UPLOAD_ENDPOINT) {
// custom endpoint for providers other than AWS
const url = new URL(process.env.S3_UPLOAD_ENDPOINT);
remotePatterns.push({
hostname: url.hostname,
})
} else if (process.env.S3_UPLOAD_BUCKET && process.env.S3_UPLOAD_REGION) {
// default provider
remotePatterns.push({
hostname: `${process.env.S3_UPLOAD_BUCKET}.s3.${process.env.S3_UPLOAD_REGION}.amazonaws.com`,
})
}
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
images: { images: {
remotePatterns: remotePatterns
process.env.S3_UPLOAD_BUCKET && process.env.S3_UPLOAD_REGION
? [
{
hostname: `${process.env.S3_UPLOAD_BUCKET}.s3.${process.env.S3_UPLOAD_REGION}.amazonaws.com`,
},
]
: [],
}, },
} }

View File

@@ -1,4 +1,5 @@
import { randomId } from '@/lib/api' import { randomId } from '@/lib/api'
import { env } from '@/lib/env'
import { POST as route } from 'next-s3-upload/route' import { POST as route } from 'next-s3-upload/route'
export const POST = route.configure({ export const POST = route.configure({
@@ -8,4 +9,7 @@ export const POST = route.configure({
const random = randomId() const random = randomId()
return `document-${timestamp}-${random}${extension.toLowerCase()}` return `document-${timestamp}-${random}${extension.toLowerCase()}`
}, },
endpoint: env.S3_UPLOAD_ENDPOINT,
// forcing path style is only necessary for providers other than AWS
forcePathStyle: !!env.S3_UPLOAD_ENDPOINT,
}) })

View File

@@ -18,7 +18,7 @@ import { useToast } from '@/components/ui/use-toast'
import { randomId } from '@/lib/api' import { randomId } from '@/lib/api'
import { ExpenseFormValues } from '@/lib/schemas' import { ExpenseFormValues } from '@/lib/schemas'
import { Loader2, Plus, Trash, X } from 'lucide-react' import { Loader2, Plus, Trash, X } from 'lucide-react'
import { getImageData, useS3Upload } from 'next-s3-upload' import { getImageData, usePresignedUpload } from 'next-s3-upload'
import Image from 'next/image' import Image from 'next/image'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
@@ -29,7 +29,7 @@ type Props = {
export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) { export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
const [pending, setPending] = useState(false) const [pending, setPending] = useState(false)
const { FileInput, openFileDialog, uploadToS3 } = useS3Upload() const { FileInput, openFileDialog, uploadToS3 } = usePresignedUpload() // use presigned uploads to addtionally support providers other than AWS
const { toast } = useToast() const { toast } = useToast()
const handleFileChange = async (file: File) => { const handleFileChange = async (file: File) => {

View File

@@ -17,12 +17,14 @@ const envSchema = z
S3_UPLOAD_SECRET: z.string().optional(), S3_UPLOAD_SECRET: z.string().optional(),
S3_UPLOAD_BUCKET: z.string().optional(), S3_UPLOAD_BUCKET: z.string().optional(),
S3_UPLOAD_REGION: z.string().optional(), S3_UPLOAD_REGION: z.string().optional(),
S3_UPLOAD_ENDPOINT: z.string().optional(),
NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT: z.coerce.boolean().default(false), NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT: z.coerce.boolean().default(false),
OPENAI_API_KEY: z.string().optional(), OPENAI_API_KEY: z.string().optional(),
}) })
.superRefine((env, ctx) => { .superRefine((env, ctx) => {
if ( if (
env.NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS && env.NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS &&
// S3_UPLOAD_ENDPOINT is fully optional as it will only be used for providers other than AWS
(!env.S3_UPLOAD_BUCKET || (!env.S3_UPLOAD_BUCKET ||
!env.S3_UPLOAD_KEY || !env.S3_UPLOAD_KEY ||
!env.S3_UPLOAD_REGION || !env.S3_UPLOAD_REGION ||