Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions DEPLOYMENT_FIX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Fixed Vercel Deployment Issues

## Issues Resolved

### 1. Build and Linting Errors ✅
- Fixed ESLint configuration for Node.js environment
- Resolved React component syntax errors
- Fixed CSS @import placement issues
- Cleaned up unused variables and imports

### 2. API URL Configuration ✅
- Created configurable API URL system (`src/lib/config.js`)
- Replaced all hardcoded `localhost:3001` references
- Added `VITE_API_URL` environment variable to `vercel.json`

### 3. API Server Structure ✅
- Updated API server to export for Vercel compatibility
- Fixed environment variable loading for production
- Updated CORS to include production domains

## Deployment Instructions

### Frontend Deployment (Vercel)
1. Connect your GitHub repository to Vercel
2. Set these environment variables in Vercel Dashboard:
```
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-supabase-anon-key
VITE_STRIPE_PUBLISHABLE_KEY=your-stripe-publishable-key
VITE_API_URL=https://your-api-domain.vercel.app
```
3. Deploy with these settings:
- Framework: React (Vite)
- Build command: `npm run build`
- Output directory: `dist`

### API Deployment (Separate Vercel Project)
1. Create a new Vercel project for the `/api` folder
2. Set these environment variables:
```
OPENAI_API_KEY=your-openai-key
VITE_SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
STRIPE_SECRET_KEY=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-webhook-secret
STRIPE_CREATOR_PRICE_ID=your-creator-price-id
STRIPE_PRO_PRICE_ID=your-pro-price-id
FRONTEND_URL=https://your-frontend-domain.vercel.app
NODE_ENV=production
```

### Important Notes
- The API and frontend must be deployed as **separate Vercel projects**
- Update the `VITE_API_URL` to point to your deployed API domain
- The build now completes successfully without errors
- All environment variables are properly configured in `vercel.json`

## Test Your Deployment
1. Build locally: `npm run build` (should complete without errors)
2. Deploy frontend to Vercel
3. Deploy API to Vercel (separate project)
4. Update environment variables with actual values
5. Test the application functionality
6 changes: 3 additions & 3 deletions api/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY || 'demo-servic
let supabase = null
try {
supabase = createClient(supabaseUrl, supabaseServiceKey)
} catch (error) {
} catch (_error) {
console.log('Supabase client initialization failed (demo mode)')
}

Expand All @@ -34,7 +34,7 @@ const adminMiddleware = async (req, res, next) => {
}

next()
} catch (error) {
} catch (_error) {
res.status(500).json({ error: 'Admin verification failed' })
}
}
Expand All @@ -51,7 +51,7 @@ router.get('/analytics', async (req, res) => {
}

// Get total users count
const { count: totalUsers } = await supabase
const { count: _totalUsers } = await supabase
.from('content_submissions')
.select('user_id', { count: 'exact', head: true })

Expand Down
2 changes: 1 addition & 1 deletion api/routes/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ router.post('/process', async (req, res) => {
// Validate URL format
try {
new URL(url)
} catch (error) {
} catch (_error) {
return res.status(400).json({ error: 'Invalid URL format' })
}

Expand Down
28 changes: 21 additions & 7 deletions api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import adminRoutes from './routes/admin.js'
import authMiddleware from './middleware/auth.js'

// Load environment variables
dotenv.config({ path: '../.env.local' })
if (process.env.NODE_ENV !== 'production') {
dotenv.config({ path: '../.env.local' })
}

const app = express()
const PORT = process.env.PORT || 3001
Expand All @@ -21,7 +23,13 @@ export const supabase = createClient(

// Middleware
app.use(cors({
origin: ['http://localhost:5173', 'http://localhost:3000'],
origin: [
'http://localhost:5173',
'http://localhost:3000',
'https://contentflow.vercel.app',
'https://contentflow-djtlb.vercel.app',
process.env.FRONTEND_URL
].filter(Boolean),
credentials: true
}))
app.use(express.json({ limit: '10mb' }))
Expand All @@ -43,7 +51,7 @@ app.get('/api/health', (req, res) => {
})

// Error handling middleware
app.use((error, req, res, next) => {
app.use((error, req, res, _next) => {
console.error('API Error:', error)
res.status(500).json({
error: 'Internal server error',
Expand All @@ -56,7 +64,13 @@ app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' })
})

app.listen(PORT, () => {
console.log(`🚀 API server running on http://localhost:${PORT}`)
console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`)
})
// Start server only in development
if (process.env.NODE_ENV !== 'production') {
app.listen(PORT, () => {
console.log(`🚀 API server running on http://localhost:${PORT}`)
console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`)
})
}

// Export for Vercel
export default app
2 changes: 1 addition & 1 deletion api/services/aiOrchestrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class AIOrchestrator {
/**
* Google Gemini integration (placeholder for enterprise expansion)
*/
async generateWithGemini(prompt, options) {
async generateWithGemini(_prompt, _options) {
// Implementation for Google Gemini API
throw new Error('Gemini integration coming soon in enterprise version')
}
Expand Down
41 changes: 0 additions & 41 deletions dist/assets/index-BTgx3oLx.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/assets/index-Dj7fSDCK.css

This file was deleted.

1 change: 0 additions & 1 deletion dist/assets/router-Dyxtdz5J.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/assets/supabase-Ch0sirx8.js

This file was deleted.

20 changes: 0 additions & 20 deletions dist/assets/ui-CZBkdTqY.js

This file was deleted.

21 changes: 0 additions & 21 deletions dist/assets/vendor-T8osx3Jj.js

This file was deleted.

12 changes: 6 additions & 6 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ContentFlow - AI Content Repurposing Tool</title>
<script type="module" crossorigin src="/assets/index-BTgx3oLx.js"></script>
<link rel="modulepreload" crossorigin href="/assets/vendor-T8osx3Jj.js">
<link rel="modulepreload" crossorigin href="/assets/ui-CZBkdTqY.js">
<link rel="modulepreload" crossorigin href="/assets/supabase-Ch0sirx8.js">
<link rel="modulepreload" crossorigin href="/assets/router-Dyxtdz5J.js">
<link rel="stylesheet" crossorigin href="/assets/index-Dj7fSDCK.css">
<script type="module" crossorigin src="/assets/index-BMQAZOP-.js"></script>
<link rel="modulepreload" crossorigin href="/assets/vendor-NuYkQVOp.js">
<link rel="modulepreload" crossorigin href="/assets/ui-B1g3F0Qu.js">
<link rel="modulepreload" crossorigin href="/assets/supabase-B-TuLt8T.js">
<link rel="modulepreload" crossorigin href="/assets/router-G9zlSsQi.js">
<link rel="stylesheet" crossorigin href="/assets/index-DkhEkmAB.css">
</head>
<body>
<div id="root"></div>
Expand Down
21 changes: 19 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import reactRefresh from 'eslint-plugin-react-refresh'

export default [
{ ignores: ['dist'] },
// Frontend files (React/Vite)
{
files: ['**/*.{js,jsx}'],
files: ['src/**/*.{js,jsx}', 'vite.config.js'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
globals: { ...globals.browser, ...globals.node },
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
Expand All @@ -30,4 +31,20 @@ export default [
],
},
},
// Backend API files (Node.js)
{
files: ['api/**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.node,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
},
rules: {
...js.configs.recommended.rules,
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]', argsIgnorePattern: '^_' }],
},
},
]
7 changes: 4 additions & 3 deletions src/components/AdminDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'
import { supabase } from '../App'
import { apiUrl } from '../lib/config'
import Layout from './Layout'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
Expand Down Expand Up @@ -65,7 +66,7 @@ const AdminDashboard = ({ user }) => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) return

const response = await fetch('http://localhost:3001/api/admin/analytics', {
const response = await fetch(apiUrl('/api/admin/analytics'), {
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json'
Expand All @@ -89,7 +90,7 @@ const AdminDashboard = ({ user }) => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) return

const response = await fetch('http://localhost:3001/api/admin/promos', {
const response = await fetch(apiUrl('/api/admin/promos'), {
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json'
Expand All @@ -114,7 +115,7 @@ const AdminDashboard = ({ user }) => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) throw new Error('No active session')

const response = await fetch('http://localhost:3001/api/admin/promos', {
const response = await fetch(apiUrl('/api/admin/promos'), {
method: 'POST',
headers: {
'Authorization': `Bearer ${session.access_token}`,
Expand Down
3 changes: 2 additions & 1 deletion src/components/ContentHistory.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'
import { supabase } from '../App'
import { apiUrl } from '../lib/config'
import Layout from './Layout'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
Expand Down Expand Up @@ -42,7 +43,7 @@ const ContentHistory = ({ user }) => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) return

const response = await fetch('http://localhost:3001/api/content/history?limit=50', {
const response = await fetch(apiUrl('/api/content/history?limit=50'), {
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json'
Expand Down
7 changes: 4 additions & 3 deletions src/components/Dashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { supabase } from '../App'
import { apiUrl } from '../lib/config'
import Layout from './Layout'
import PromoBanner from './PromoBanner'
import { Button } from '@/components/ui/button'
Expand Down Expand Up @@ -43,7 +44,7 @@ const Dashboard = ({ user }) => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) return

const response = await fetch('http://localhost:3001/api/content/history', {
const response = await fetch(apiUrl('/api/content/history'), {
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json'
Expand All @@ -64,7 +65,7 @@ const Dashboard = ({ user }) => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) return

const response = await fetch('http://localhost:3001/api/content/usage/stats', {
const response = await fetch(apiUrl('/api/content/usage/stats'), {
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json'
Expand Down Expand Up @@ -93,7 +94,7 @@ const Dashboard = ({ user }) => {
}

// Call our backend API to process the content
const response = await fetch('http://localhost:3001/api/content/process', {
const response = await fetch(apiUrl('/api/content/process'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
2 changes: 1 addition & 1 deletion src/components/Navigation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const Navigation = ({ user, onSignOut }) => {
const location = useLocation()

// Check if user is admin (hidden from UI)
const isAdmin = user && ['admin@contentflow.com', 'sallykamari61@gmail.com'].includes(user.email)
const _isAdmin = user && ['admin@contentflow.com', 'sallykamari61@gmail.com'].includes(user.email)

const navItems = [
{ path: '/dashboard', label: 'Dashboard', icon: Home },
Expand Down
3 changes: 2 additions & 1 deletion src/components/PromoBanner.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'
import { supabase } from '../App'
import { apiUrl } from '../lib/config'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { X, Megaphone, Gift, Zap, AlertTriangle } from 'lucide-react'
Expand All @@ -18,7 +19,7 @@ const PromoBanner = () => {
const { data: { session } } = await supabase.auth.getSession()
if (!session) return

const response = await fetch('http://localhost:3001/api/admin/promos', {
const response = await fetch(apiUrl('/api/admin/promos'), {
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json'
Expand Down
2 changes: 1 addition & 1 deletion src/components/SettingsPage.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React from 'react'
import Layout from './Layout'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
Expand Down
Loading