Add Stuffbox account connection, encrypted token lifecycle, direct media uploads, storage settings, and S3 fallback

Hop-State: A_06FP7VQ2F5KZQAY57YEBZV8
Hop-Proposal: R_06FP7VPDD7MJH2Z0013D7SR
Hop-Task: T_06FP7QPNG91NT6WBGJD5MJG
Hop-Attempt: AT_06FP7QPNGAEEAEHP3Q7CNEG
This commit is contained in:
2026-07-14 21:00:27 -07:00
committed by Hop
parent 752298a405
commit 9f0bfb59ab
26 changed files with 7657 additions and 168 deletions
+13 -21
View File
@@ -8,6 +8,7 @@ import { VideoEmbed } from '@/components/VideoEmbed';
import { useFormattedHandle } from '@/lib/utils/handle';
import { useAuth } from '@/lib/contexts/AuthContext';
import { StorageConfigurationPrompt } from '@/components/StorageConfigurationPrompt';
import { MediaUploadError, uploadMediaFile } from '@/lib/stuffbox/browser-upload';
interface MediaAttachment extends Attachment {
mimeType?: string;
@@ -131,31 +132,22 @@ export function Compose({ onPost, replyingTo, onCancelReply, placeholder = "What
for (const file of selectedFiles) {
try {
const formData = new FormData();
formData.append('file', file);
const res = await fetch('/api/media/upload', {
method: 'POST',
body: formData,
});
const data = await res.json();
if (!res.ok || !data.media?.id) {
if (data.code === 'STORAGE_NOT_CONFIGURED') {
setPendingStorageFiles(selectedFiles);
setShowStorageConfiguration(true);
setIsUploading(false);
return;
}
throw new Error(data.error || 'Upload failed');
}
const media = await uploadMediaFile(file);
uploaded.push({
id: data.media.id,
url: data.media.url || data.url,
altText: data.media.altText ?? null,
mimeType: data.media.mimeType ?? file.type,
id: media.id,
url: media.url,
altText: media.altText ?? null,
mimeType: media.mimeType ?? file.type,
});
} catch (error) {
if (error instanceof MediaUploadError && error.code === 'STORAGE_NOT_CONFIGURED') {
setAttachments((prev) => [...prev, ...uploaded].slice(0, 4));
setPendingStorageFiles(selectedFiles.slice(uploaded.length));
setShowStorageConfiguration(true);
setIsUploading(false);
return;
}
console.error('Upload failed', error);
setUploadError('One or more uploads failed. Try again.');
}