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
+14 -24
View File
@@ -8,6 +8,7 @@ import { StorageConfigurationPrompt } from '@/components/StorageConfigurationPro
import { useToast } from '@/lib/contexts/ToastContext';
import { useAccentColor } from '@/lib/contexts/AccentColorContext';
import { refreshStorageSession } from '@/lib/storage/client';
import { MediaUploadError, uploadMediaFile } from '@/lib/stuffbox/browser-upload';
export default function AdminPage() {
const { showToast } = useToast();
@@ -105,33 +106,11 @@ export default function AdminPage() {
setIsUploadingBanner(true);
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.url) {
if (data.code === 'STORAGE_NOT_CONFIGURED' && allowPrompt) {
setPendingBannerFile(file);
setShowBannerStorageConfiguration(true);
return;
}
if (res.status === 401 && allowPrompt) {
setPendingBannerFile(file);
setBannerPromptError('');
setShowBannerSessionPrompt(true);
return;
}
throw new Error(data.error || 'Upload failed');
}
const media = await uploadMediaFile(file);
const nextSettings = {
...nodeSettings,
bannerUrl: data.media?.url || data.url,
bannerUrl: media.url,
};
setNodeSettings(nextSettings);
await handleSaveSettings(nextSettings);
@@ -140,6 +119,17 @@ export default function AdminPage() {
setBannerPassword('');
setBannerPromptError('');
} catch (error) {
if (error instanceof MediaUploadError && error.code === 'STORAGE_NOT_CONFIGURED' && allowPrompt) {
setPendingBannerFile(file);
setShowBannerStorageConfiguration(true);
return;
}
if (error instanceof MediaUploadError && error.status === 401 && allowPrompt) {
setPendingBannerFile(file);
setBannerPromptError('');
setShowBannerSessionPrompt(true);
return;
}
console.error('Banner upload failed', error);
setBannerUploadError(error instanceof Error ? error.message : 'Upload failed. Please try again.');
} finally {