From 2014ff9fce0bcc6b2e842833dea680af9a8050ee Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 30 Nov 2025 00:35:34 +0000 Subject: [PATCH] feat(import): add multi-site import modal and upload-multi API --- frontend/src/api/import.ts | 5 ++ frontend/src/components/ImportSitesModal.tsx | 90 ++++++++++++++++++++ frontend/src/pages/ImportCaddy.tsx | 14 +++ 3 files changed, 109 insertions(+) create mode 100644 frontend/src/components/ImportSitesModal.tsx diff --git a/frontend/src/api/import.ts b/frontend/src/api/import.ts index fe7366a6..eb42b0ef 100644 --- a/frontend/src/api/import.ts +++ b/frontend/src/api/import.ts @@ -40,6 +40,11 @@ export const uploadCaddyfile = async (content: string): Promise = return data; }; +export const uploadCaddyfilesMulti = async (contents: string[]): Promise => { + const { data } = await client.post('/import/upload-multi', { contents }); + return data; +}; + export const getImportPreview = async (): Promise => { const { data } = await client.get('/import/preview'); return data; diff --git a/frontend/src/components/ImportSitesModal.tsx b/frontend/src/components/ImportSitesModal.tsx new file mode 100644 index 00000000..79627d0b --- /dev/null +++ b/frontend/src/components/ImportSitesModal.tsx @@ -0,0 +1,90 @@ +import React, { useState } from 'react' +import { uploadCaddyfilesMulti } from '../api/import' + +type Props = { + visible: boolean + onClose: () => void + onUploaded?: () => void +} + +export default function ImportSitesModal({ visible, onClose, onUploaded }: Props) { + const [sites, setSites] = useState(['']) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + + if (!visible) return null + + const setSite = (index: number, value: string) => { + const s = [...sites] + s[index] = value + setSites(s) + } + + const addSite = () => setSites(prev => [...prev, '']) + const removeSite = (index: number) => setSites(prev => prev.filter((_, i) => i !== index)) + + const handleSubmit = async () => { + setError(null) + setLoading(true) + try { + const cleaned = sites.map(s => s || '') + await uploadCaddyfilesMulti(cleaned) + setLoading(false) + onUploaded && onUploaded() + onClose() + } catch (err: any) { + setError(err?.message || 'Upload failed') + setLoading(false) + } + } + + return ( +
+
+
+

Multi-site Import

+

Add each site's Caddyfile content separately, then parse them together.

+ +
+ {sites.map((s, idx) => ( +
+
+
Site {idx + 1}
+
+ {sites.length > 1 && ( + + )} +
+
+