aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--next.config.js29
-rw-r--r--src/app/api/s3-upload/route.ts4
-rw-r--r--src/components/expense-documents-input.tsx4
-rw-r--r--src/lib/env.ts2
5 files changed, 35 insertions, 10 deletions
diff --git a/README.md b/README.md
index 9ce6f73..0115ac5 100644
--- a/README.md
+++ b/README.md
@@ -74,6 +74,12 @@ S3_UPLOAD_BUCKET=name-of-s3-bucket
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
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).
diff --git a/next.config.js b/next.config.js
index 6d6b0c3..89faa5d 100644
--- a/next.config.js
+++ b/next.config.js
@@ -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} */
const nextConfig = {
images: {
- 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`,
- },
- ]
- : [],
+ remotePatterns
},
}
diff --git a/src/app/api/s3-upload/route.ts b/src/app/api/s3-upload/route.ts
index 567cd40..ee3fb72 100644
--- a/src/app/api/s3-upload/route.ts
+++ b/src/app/api/s3-upload/route.ts
@@ -1,4 +1,5 @@
import { randomId } from '@/lib/api'
+import { env } from '@/lib/env'
import { POST as route } from 'next-s3-upload/route'
export const POST = route.configure({
@@ -8,4 +9,7 @@ export const POST = route.configure({
const random = randomId()
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,
})
diff --git a/src/components/expense-documents-input.tsx b/src/components/expense-documents-input.tsx
index b9cda33..7b69a6d 100644
--- a/src/components/expense-documents-input.tsx
+++ b/src/components/expense-documents-input.tsx
@@ -18,7 +18,7 @@ import { useToast } from '@/components/ui/use-toast'
import { randomId } from '@/lib/api'
import { ExpenseFormValues } from '@/lib/schemas'
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 { useEffect, useState } from 'react'
@@ -29,7 +29,7 @@ type Props = {
export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
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 handleFileChange = async (file: File) => {
diff --git a/src/lib/env.ts b/src/lib/env.ts
index 33b835d..f1d59db 100644
--- a/src/lib/env.ts
+++ b/src/lib/env.ts
@@ -17,12 +17,14 @@ const envSchema = z
S3_UPLOAD_SECRET: z.string().optional(),
S3_UPLOAD_BUCKET: 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),
OPENAI_API_KEY: z.string().optional(),
})
.superRefine((env, ctx) => {
if (
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_KEY ||
!env.S3_UPLOAD_REGION ||