|
@@ -72,6 +72,195 @@ serve(async (req) => {
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // POST /api/stores/finalize-shoprenter - Finalize ShopRenter installation
|
|
|
|
|
+ if (path === 'stores/finalize-shoprenter' && req.method === 'POST') {
|
|
|
|
|
+ const { installation_id } = await req.json()
|
|
|
|
|
+
|
|
|
|
|
+ if (!installation_id) {
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({ error: 'installation_id is required' }),
|
|
|
|
|
+ { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
|
|
|
|
|
+ const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey)
|
|
|
|
|
+
|
|
|
|
|
+ // Get pending installation
|
|
|
|
|
+ const { data: installation, error: installError } = await supabaseAdmin
|
|
|
|
|
+ .from('pending_shoprenter_installs')
|
|
|
|
|
+ .select('*')
|
|
|
|
|
+ .eq('installation_id', installation_id)
|
|
|
|
|
+ .single()
|
|
|
|
|
+
|
|
|
|
|
+ if (installError || !installation) {
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({ error: 'Installation not found or expired' }),
|
|
|
|
|
+ { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Check if expired
|
|
|
|
|
+ if (new Date(installation.expires_at) < new Date()) {
|
|
|
|
|
+ await supabaseAdmin.from('pending_shoprenter_installs').delete().eq('installation_id', installation_id)
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({ error: 'Installation expired. Please try again.' }),
|
|
|
|
|
+ { status: 410, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Create store record
|
|
|
|
|
+ const { data: newStore, error: storeError } = await supabaseAdmin
|
|
|
|
|
+ .from('stores')
|
|
|
|
|
+ .insert({
|
|
|
|
|
+ user_id: user.id,
|
|
|
|
|
+ platform_name: 'shoprenter',
|
|
|
|
|
+ store_name: installation.shopname,
|
|
|
|
|
+ store_url: `https://${installation.shopname}.shoprenter.hu`,
|
|
|
|
|
+ api_key: installation.access_token,
|
|
|
|
|
+ api_secret: installation.refresh_token,
|
|
|
|
|
+ scopes: installation.scopes || [],
|
|
|
|
|
+ alt_data: {
|
|
|
|
|
+ token_type: installation.token_type,
|
|
|
|
|
+ expires_in: installation.expires_in,
|
|
|
|
|
+ connectedAt: new Date().toISOString()
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .select('id')
|
|
|
|
|
+ .single()
|
|
|
|
|
+
|
|
|
|
|
+ if (storeError || !newStore) {
|
|
|
|
|
+ console.error('[API] Error creating store:', storeError)
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({ error: 'Failed to save store credentials' }),
|
|
|
|
|
+ { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Delete pending installation
|
|
|
|
|
+ await supabaseAdmin.from('pending_shoprenter_installs').delete().eq('installation_id', installation_id)
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`[API] ShopRenter store finalized: ${installation.shopname}`)
|
|
|
|
|
+
|
|
|
|
|
+ // Trigger auto-sync in background
|
|
|
|
|
+ const triggerSyncUrl = `${supabaseUrl}/functions/v1/trigger-sync`
|
|
|
|
|
+ fetch(triggerSyncUrl, {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${supabaseServiceKey}`,
|
|
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
|
|
+ },
|
|
|
|
|
+ body: JSON.stringify({ store_id: newStore.id })
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => console.log(`[API] Auto-sync triggered for ShopRenter store ${newStore.id}`))
|
|
|
|
|
+ .catch(err => console.error(`[API] Failed to trigger auto-sync:`, err))
|
|
|
|
|
+
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({
|
|
|
|
|
+ success: true,
|
|
|
|
|
+ message: 'Store connected successfully',
|
|
|
|
|
+ store_id: newStore.id
|
|
|
|
|
+ }),
|
|
|
|
|
+ { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // GET /api/stores/:id/sync-status - Get sync status for a store
|
|
|
|
|
+ if (path.match(/^stores\/[^\/]+\/sync-status$/) && req.method === 'GET') {
|
|
|
|
|
+ const storeId = path.split('/')[1]
|
|
|
|
|
+
|
|
|
|
|
+ const { data: store, error: storeError } = await supabase
|
|
|
|
|
+ .from('stores')
|
|
|
|
|
+ .select('sync_status, sync_started_at, sync_completed_at, sync_error')
|
|
|
|
|
+ .eq('id', storeId)
|
|
|
|
|
+ .eq('user_id', user.id)
|
|
|
|
|
+ .single()
|
|
|
|
|
+
|
|
|
|
|
+ if (storeError || !store) {
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({ error: 'Store not found or access denied' }),
|
|
|
|
|
+ { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({
|
|
|
|
|
+ success: true,
|
|
|
|
|
+ status: store.sync_status || 'idle',
|
|
|
|
|
+ started_at: store.sync_started_at,
|
|
|
|
|
+ completed_at: store.sync_completed_at,
|
|
|
|
|
+ error: store.sync_error
|
|
|
|
|
+ }),
|
|
|
|
|
+ { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // POST /api/stores/:id/sync - Trigger manual sync for a store
|
|
|
|
|
+ if (path.match(/^stores\/[^\/]+\/sync$/) && req.method === 'POST') {
|
|
|
|
|
+ const storeId = path.split('/')[1]
|
|
|
|
|
+
|
|
|
|
|
+ // Verify store ownership
|
|
|
|
|
+ const { data: store, error: storeError } = await supabase
|
|
|
|
|
+ .from('stores')
|
|
|
|
|
+ .select('id, platform_name, sync_status')
|
|
|
|
|
+ .eq('id', storeId)
|
|
|
|
|
+ .eq('user_id', user.id)
|
|
|
|
|
+ .single()
|
|
|
|
|
+
|
|
|
|
|
+ if (storeError || !store) {
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({ error: 'Store not found or access denied' }),
|
|
|
|
|
+ { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Check if sync is already running
|
|
|
|
|
+ if (store.sync_status === 'syncing') {
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({
|
|
|
|
|
+ success: false,
|
|
|
|
|
+ error: 'Sync already in progress',
|
|
|
|
|
+ status: 'syncing'
|
|
|
|
|
+ }),
|
|
|
|
|
+ { status: 409, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Trigger sync via trigger-sync function
|
|
|
|
|
+ const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
|
|
|
|
|
+ const triggerSyncUrl = `${supabaseUrl}/functions/v1/trigger-sync`
|
|
|
|
|
+
|
|
|
|
|
+ const syncResponse = await fetch(triggerSyncUrl, {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${supabaseServiceKey}`,
|
|
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
|
|
+ },
|
|
|
|
|
+ body: JSON.stringify({ store_id: storeId })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const syncResult = await syncResponse.json()
|
|
|
|
|
+
|
|
|
|
|
+ if (!syncResponse.ok) {
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({
|
|
|
|
|
+ success: false,
|
|
|
|
|
+ error: syncResult.error || 'Failed to trigger sync'
|
|
|
|
|
+ }),
|
|
|
|
|
+ { status: syncResponse.status, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify({
|
|
|
|
|
+ success: true,
|
|
|
|
|
+ message: 'Sync triggered successfully',
|
|
|
|
|
+ status: 'syncing'
|
|
|
|
|
+ }),
|
|
|
|
|
+ { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// DELETE /api/stores/{id} - Delete a store
|
|
// DELETE /api/stores/{id} - Delete a store
|
|
|
if (path.startsWith('stores/') && req.method === 'DELETE') {
|
|
if (path.startsWith('stores/') && req.method === 'DELETE') {
|
|
|
const storeId = path.replace('stores/', '')
|
|
const storeId = path.replace('stores/', '')
|