diff --git a/docker-compose.yml b/docker-compose.yml index b42f2fa..393c408 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -72,6 +72,9 @@ services: # Port configuration (auto or specific port) PORT: ${PORT:-3000} + # S3 Storage public URL (for custom S3 providers like R2, B2, Contabo) + STORAGE_PUBLIC_BASE_URL: ${STORAGE_PUBLIC_BASE_URL:-} + # Node environment NODE_ENV: production volumes: diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 1f07db1..9d237cb 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -15,6 +15,7 @@ const registerSchema = z.object({ // S3-compatible storage credentials storageProvider: z.string().min(1), storageEndpoint: z.string().nullable().optional(), + storagePublicBaseUrl: z.string().nullable().optional(), storageRegion: z.string().min(1), storageBucket: z.string().min(1), storageAccessKey: z.string().min(10), @@ -71,6 +72,7 @@ export async function POST(request: Request) { data.displayName, data.storageProvider, data.storageEndpoint || null, + data.storagePublicBaseUrl || null, data.storageRegion, data.storageBucket, data.storageAccessKey, diff --git a/src/app/api/bots/route.ts b/src/app/api/bots/route.ts index 20de418..f59c3e7 100644 --- a/src/app/api/bots/route.ts +++ b/src/app/api/bots/route.ts @@ -78,6 +78,7 @@ export async function POST(request: Request) { botAvatarUrl = await generateAndUploadAvatarToUserStorage( botHandle, user.storageEndpoint || undefined, + user.storagePublicBaseUrl || undefined, user.storageRegion || 'auto', user.storageBucket, accessKeyId, diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts new file mode 100644 index 0000000..3fa48ad --- /dev/null +++ b/src/app/api/config/route.ts @@ -0,0 +1,7 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + return NextResponse.json({ + domain: process.env.NEXT_PUBLIC_NODE_DOMAIN || process.env.NODE_DOMAIN || 'localhost:3000', + }); +} diff --git a/src/app/api/media/upload/route.ts b/src/app/api/media/upload/route.ts index 47c9f44..c70193e 100644 --- a/src/app/api/media/upload/route.ts +++ b/src/app/api/media/upload/route.ts @@ -64,6 +64,7 @@ export async function POST(req: NextRequest) { file.type, user.storageProvider as any, user.storageEndpoint, + user.storagePublicBaseUrl, user.storageRegion || 'us-east-1', user.storageBucket || '', user.storageAccessKeyEncrypted, diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index 52d31e2..fed52e7 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -4,7 +4,7 @@ import { useState, useEffect, useRef } from 'react'; import { useAuth } from '@/lib/contexts/AuthContext'; import { signedAPI } from '@/lib/api/signed-fetch'; import { ArrowLeft, Send, Loader2, MessageCircle, Search, Plus, Trash2, MoreVertical } from 'lucide-react'; -import { formatFullHandle } from '@/lib/utils/handle'; +import { useFormattedHandle } from '@/lib/utils/handle'; import { useRouter, useSearchParams } from 'next/navigation'; interface Conversation { @@ -43,6 +43,7 @@ export default function ChatPage() { // Chat Data State const [conversations, setConversations] = useState([]); const [selectedConversation, setSelectedConversation] = useState(null); + const selectedHandle = selectedConversation ? useFormattedHandle(selectedConversation.participant2.handle) : ''; const [messages, setMessages] = useState([]); const [newMessage, setNewMessage] = useState(''); const [loading, setLoading] = useState(true); @@ -389,7 +390,7 @@ export default function ChatPage() {
{selectedConversation.participant2.displayName}
- {formatFullHandle(selectedConversation.participant2.handle)} + {selectedHandle}