Skip to content

Commit b34dfb3

Browse files
FlowEvolveclaude
andcommitted
fix: configure frontend for Cloudflare Pages deployment
Configuration Changes: - Updated deployment workflow from Vercel to Cloudflare Pages - Configured Next.js for static export with Cloudflare Pages - Connected frontend to existing Cloudflare Workers backend API - Added JSON API endpoint to workers for frontend consumption Technical Details: - Next.js static export mode with trailingSlash and unoptimized images - Frontend API client points to existing workers backend - New /api/generate endpoint in workers returns JSON instead of HTML - Maintains backward compatibility with original HTML endpoints Deployment Strategy: - Frontend: Cloudflare Pages (static export) - Backend API: Existing Cloudflare Workers (modularized) - Uses same CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID secrets 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent f7646e1 commit b34dfb3

File tree

4 files changed

+146
-34
lines changed

4 files changed

+146
-34
lines changed

.github/workflows/deploy-frontend.yml

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Deploy Frontend to Vercel
1+
name: Deploy Frontend to Cloudflare Pages
22

33
on:
44
push:
@@ -22,6 +22,9 @@ jobs:
2222
runs-on: ubuntu-latest
2323
name: Deploy Frontend to Staging
2424
environment: staging
25+
env:
26+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
27+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
2528
steps:
2629
- name: Checkout
2730
uses: actions/checkout@v4
@@ -33,24 +36,31 @@ jobs:
3336
cache: 'npm'
3437
cache-dependency-path: frontend/package-lock.json
3538

39+
- name: "Preflight: verify Cloudflare secrets (staging)"
40+
run: |
41+
if [ -z "${CLOUDFLARE_API_TOKEN}" ] || [ -z "${CLOUDFLARE_ACCOUNT_ID}" ]; then
42+
echo "::error::Missing Cloudflare secrets. Ensure CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are set in repository or environment secrets."
43+
exit 1
44+
fi
45+
3646
- name: Install frontend dependencies
3747
run: npm ci
3848
working-directory: frontend
3949

40-
- name: Build frontend
50+
- name: Build frontend for Cloudflare Pages
4151
run: npm run build
4252
working-directory: frontend
4353
env:
4454
NODE_ENV: production
4555

46-
- name: Deploy to Vercel (Staging)
47-
uses: amondnet/vercel-action@v25
56+
- name: Deploy to Cloudflare Pages (Staging)
57+
uses: cloudflare/pages-action@v1
4858
with:
49-
vercel-token: ${{ secrets.VERCEL_TOKEN }}
50-
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
51-
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
52-
working-directory: frontend
53-
scope: ${{ secrets.VERCEL_ORG_ID }}
59+
apiToken: ${{ env.CLOUDFLARE_API_TOKEN }}
60+
accountId: ${{ env.CLOUDFLARE_ACCOUNT_ID }}
61+
projectName: supabase-configurator-staging
62+
directory: frontend/out
63+
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
5464

5565
- name: Comment PR with staging URL
5666
if: github.event_name == 'pull_request'
@@ -61,7 +71,7 @@ jobs:
6171
issue_number: context.issue.number,
6272
owner: context.repo.owner,
6373
repo: context.repo.repo,
64-
body: '🚀 Frontend staging deployment complete!\\n\\n**Preview URL:** Available in Vercel deployment logs\\n\\nTest your changes before merging to production.'
74+
body: '🚀 Frontend staging deployment complete!\\n\\n**Preview URL:** https://supabase-configurator-staging.pages.dev\\n\\nTest your changes before merging to production.'
6575
})
6676
6777
# Deploy to production on main branch
@@ -70,6 +80,9 @@ jobs:
7080
runs-on: ubuntu-latest
7181
name: Deploy Frontend to Production
7282
environment: production
83+
env:
84+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
85+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
7386
steps:
7487
- name: Checkout
7588
uses: actions/checkout@v4
@@ -81,6 +94,13 @@ jobs:
8194
cache: 'npm'
8295
cache-dependency-path: frontend/package-lock.json
8396

97+
- name: "Preflight: verify Cloudflare secrets (production)"
98+
run: |
99+
if [ -z "${CLOUDFLARE_API_TOKEN}" ] || [ -z "${CLOUDFLARE_ACCOUNT_ID}" ]; then
100+
echo "::error::Missing Cloudflare secrets. Ensure CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are set in repository or environment secrets."
101+
exit 1
102+
fi
103+
84104
- name: Install frontend dependencies
85105
run: npm ci
86106
working-directory: frontend
@@ -89,35 +109,35 @@ jobs:
89109
run: npm run typecheck
90110
working-directory: frontend
91111

92-
- name: Run linting
112+
- name: Run linting
93113
run: npm run lint
94114
working-directory: frontend
95115

96-
- name: Build frontend
116+
- name: Build frontend for Cloudflare Pages
97117
run: npm run build
98118
working-directory: frontend
99119
env:
100120
NODE_ENV: production
101121

102-
- name: Deploy to Vercel (Production)
122+
- name: Deploy to Cloudflare Pages (Production)
103123
id: deploy
104-
uses: amondnet/vercel-action@v25
124+
uses: cloudflare/pages-action@v1
105125
with:
106-
vercel-token: ${{ secrets.VERCEL_TOKEN }}
107-
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
108-
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
109-
vercel-args: '--prod'
110-
working-directory: frontend
111-
scope: ${{ secrets.VERCEL_ORG_ID }}
126+
apiToken: ${{ env.CLOUDFLARE_API_TOKEN }}
127+
accountId: ${{ env.CLOUDFLARE_ACCOUNT_ID }}
128+
projectName: supabase-configurator
129+
directory: frontend/out
130+
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
112131

113132
- name: Show deployment URL
114133
run: |
115-
echo "🚀 Frontend deployed successfully to Vercel!"
116-
echo "📍 Production URL: https://supabase-configurator.vercel.app (or your custom domain)"
134+
echo "🚀 Frontend deployed successfully to Cloudflare Pages!"
135+
echo "📍 Production URL: https://supabase-configurator.pages.dev"
136+
echo "📍 Custom domain can be configured in Cloudflare Pages dashboard"
117137
echo ""
118138
echo "🔧 Next steps:"
119-
echo " 1. Configure custom domain in Vercel dashboard"
120-
echo " 2. Update DNS records if using custom domain"
139+
echo " 1. Configure custom domain in Cloudflare Pages dashboard"
140+
echo " 2. Update DNS records to point to Cloudflare Pages"
121141
echo " 3. Enable Lighthouse testing with confirmed URL"
122142
123143
# Health check after deployment
@@ -128,20 +148,20 @@ jobs:
128148
name: Production Health Check
129149
steps:
130150
- name: Wait for deployment
131-
run: sleep 30
151+
run: sleep 45
132152

133153
- name: Health Check
134154
run: |
135155
echo "Checking production health..."
136-
# Test the deployment - update with your actual Vercel URL
137-
URL="https://supabase-configurator.vercel.app"
156+
# Test the Cloudflare Pages deployment
157+
URL="https://supabase-configurator.pages.dev"
138158
139159
if curl -f -s --max-time 10 "$URL" > /dev/null; then
140160
echo "✅ Frontend is accessible at $URL"
141161
else
142162
echo "❌ Frontend health check failed"
143163
echo "🔍 Manual check required at $URL"
144-
# Don't fail the workflow since URL might be different
164+
# Don't fail workflow since URL might be different initially
145165
fi
146166
147167
- name: Notify on failure
@@ -156,7 +176,7 @@ jobs:
156176
body: `Production frontend deployment failed for commit ${context.sha}.\\n\\nPlease investigate immediately.\\n\\n**Commit:** ${context.sha}\\n**Workflow:** ${context.workflow}\\n**Run:** ${context.runId}`
157177
})
158178
159-
# Optional: Lighthouse performance test
179+
# Lighthouse performance test
160180
lighthouse:
161181
needs: [deploy-production]
162182
if: false # Enable once URL is confirmed: github.ref == 'refs/heads/main' && github.event_name == 'push'
@@ -167,13 +187,13 @@ jobs:
167187
uses: actions/checkout@v4
168188

169189
- name: Wait for deployment
170-
run: sleep 45
190+
run: sleep 60
171191

172192
- name: Lighthouse CI
173193
uses: treosh/lighthouse-ci-action@v10
174194
with:
175195
urls: |
176-
https://supabase-configurator.vercel.app
196+
https://supabase-configurator.pages.dev
177197
configPath: './.lighthouserc.json'
178198
uploadArtifacts: true
179199
temporaryPublicStorage: true

frontend/.deploy-trigger-frontend

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
Frontend deployment trigger - Modularized code ready for deployment
1+
Frontend deployment trigger - Modularized Next.js ready for Cloudflare Pages
22

33
Modularization completed:
44
- DeploymentModal split into 3 modules (271 lines total)
55
- config-generator split into 3 modules (60 + 343 + 157 + 51 lines)
66
- All files now under 500 line limit
7-
- Ready for Vercel deployment
7+
- Configured for Cloudflare Pages static export
8+
- Using existing Cloudflare Workers backend for API calls
89

9-
Created at: 2025-08-13T15:58:00Z
10+
Updated at: 2025-08-13T16:44:00Z
1011

frontend/next.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4+
// Configure for Cloudflare Pages static export
5+
output: 'export',
6+
trailingSlash: true,
7+
images: {
8+
unoptimized: true,
9+
},
410
eslint: {
511
// Disable ESLint during builds for development
612
ignoreDuringBuilds: true,

workers/index.mjs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,91 @@ async function handleRequest(request) {
5959
}
6060
}
6161

62+
// JSON API endpoint for frontend
63+
if (path === '/api/generate' && request.method === 'POST') {
64+
const formData = await request.formData();
65+
const data = Object.fromEntries(formData.entries());
66+
67+
// Validate input
68+
const validationErrors = validateInput(data);
69+
if (validationErrors.length > 0) {
70+
return new Response(JSON.stringify({
71+
error: 'Validation failed',
72+
details: validationErrors
73+
}), {
74+
status: 400,
75+
headers: {
76+
'Content-Type': 'application/json',
77+
...corsHeaders
78+
}
79+
});
80+
}
81+
82+
// Generate secure credentials if not provided
83+
const config = {
84+
project_name: data.project_name,
85+
domain: data.domain,
86+
email: data.email,
87+
db_password: data.db_password || generatePassword(32),
88+
jwt_secret: data.jwt_secret || generateSecureSecret(64)
89+
};
90+
91+
// Generate JWT keys if not provided
92+
if (!data.anon_key || !data.service_key) {
93+
try {
94+
const anonPayload = { role: 'anon', iss: 'supabase' };
95+
const servicePayload = { role: 'service_role', iss: 'supabase' };
96+
97+
config.anon_key = data.anon_key || await generateJWT(config.jwt_secret, anonPayload);
98+
config.service_key = data.service_key || await generateJWT(config.jwt_secret, servicePayload);
99+
} catch (error) {
100+
console.error('JWT generation error:', error);
101+
return new Response(JSON.stringify({
102+
error: 'Failed to generate JWT keys: ' + error.message
103+
}), {
104+
status: 500,
105+
headers: {
106+
'Content-Type': 'application/json',
107+
...corsHeaders
108+
}
109+
});
110+
}
111+
} else {
112+
config.anon_key = data.anon_key;
113+
config.service_key = data.service_key;
114+
}
115+
116+
// Generate configuration files and return JSON
117+
try {
118+
const envContent = generateEnvFile(config);
119+
const composeContent = generateDockerCompose(config);
120+
121+
return new Response(JSON.stringify({
122+
envContent,
123+
composeContent,
124+
overrideContent: '', // Add override if needed
125+
config
126+
}), {
127+
headers: {
128+
'Content-Type': 'application/json',
129+
...corsHeaders
130+
}
131+
});
132+
133+
} catch (error) {
134+
console.error('Configuration generation error:', error);
135+
return new Response(JSON.stringify({
136+
error: 'Configuration generation failed: ' + error.message
137+
}), {
138+
status: 500,
139+
headers: {
140+
'Content-Type': 'application/json',
141+
...corsHeaders
142+
}
143+
});
144+
}
145+
}
146+
62147
if (path === '/generate' && request.method === 'POST') {
63148
const formData = await request.formData();
64149
const data = Object.fromEntries(formData.entries());

0 commit comments

Comments
 (0)