Skip to content

Commit cf6049d

Browse files
committed
fix: disable Vercel deployment protection for public access
Deployed sites were requiring Vercel login due to SSO protection being enabled by default on the Vercel team. Added disable_deployment_protection() method that sets ssoProtection to null via PATCH /v10/projects/{projectId}. Called in ensure_project() for new projects and deploy_files() for existing projects to ensure all deployments are publicly accessible.
1 parent 00f50a8 commit cf6049d

File tree

7 files changed

+306
-128
lines changed

7 files changed

+306
-128
lines changed

backend/services/vercel_deploy.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,25 @@ def _team_params(self) -> Dict[str, str]:
6565
"""Add team params to requests if team_id is configured."""
6666
return {"teamId": self.team_id} if self.team_id else {}
6767

68+
async def disable_deployment_protection(self, project_id: str) -> None:
69+
"""
70+
Disable SSO protection on a Vercel project to make deployments publicly accessible.
71+
72+
Without this, deployments may require Vercel login to view if the team
73+
has default deployment protection enabled.
74+
"""
75+
async with httpx.AsyncClient(timeout=30.0) as client:
76+
resp = await client.patch(
77+
f"{VERCEL_API_BASE}/v10/projects/{project_id}",
78+
headers=self._headers(),
79+
params=self._team_params(),
80+
json={"ssoProtection": None}
81+
)
82+
if resp.status_code not in (200, 201):
83+
logger.warning(f"Failed to disable deployment protection: {resp.status_code} {resp.text}")
84+
else:
85+
logger.info(f"Disabled deployment protection for project: {project_id}")
86+
6887
async def ensure_project(self, project_name: str, framework: str = "nextjs") -> Dict[str, Any]:
6988
"""
7089
Create or get existing Vercel project.
@@ -92,7 +111,10 @@ async def ensure_project(self, project_name: str, framework: str = "nextjs") ->
92111
json={"name": project_name, "framework": framework}
93112
)
94113
if resp.status_code in (200, 201):
95-
return resp.json()
114+
project = resp.json()
115+
# Disable deployment protection for public access
116+
await self.disable_deployment_protection(project.get('id'))
117+
return project
96118

97119
error_msg = f"Failed to create Vercel project: {resp.status_code} {resp.text}"
98120
logger.error(error_msg)
@@ -152,6 +174,10 @@ async def deploy_files(
152174
if resp.status_code in (200, 201):
153175
result = resp.json()
154176
logger.info(f"Vercel deployment created: {result.get('id')} -> {result.get('url')}")
177+
# Disable deployment protection to ensure public access (fixes existing projects too)
178+
project_id = result.get('projectId')
179+
if project_id:
180+
await self.disable_deployment_protection(project_id)
155181
return result
156182

157183
error_msg = f"Vercel deployment failed: {resp.status_code} {resp.text}"

frontend/src/components/home/sections/hero-section.tsx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -192,24 +192,23 @@ export function HeroSection() {
192192

193193
<div className="flex items-center w-full max-w-3xl gap-2 flex-wrap justify-center">
194194
<div className="w-full relative group">
195-
<div className="relative z-10 border border-zinc-800 bg-black shadow-2xl overflow-hidden px-4 py-2 transition-colors duration-300 group-focus-within:border-zinc-700">
196-
<ChatInput
197-
ref={chatInputRef}
198-
onSubmit={handleChatInputSubmit}
199-
placeholder="Describe what you need help with..."
200-
loading={isSubmitting}
201-
disabled={isSubmitting}
202-
value={inputValue}
203-
onChange={setInputValue}
204-
isLoggedIn={!!user}
205-
autoFocus={false}
206-
appType={appType}
207-
onAppTypeChange={setAppType}
208-
selectedModel={selectedModel}
209-
onModelChange={setSelectedModel}
210-
bgColor="bg-black"
211-
/>
212-
</div>
195+
<ChatInput
196+
ref={chatInputRef}
197+
onSubmit={handleChatInputSubmit}
198+
placeholder="Describe what you need help with..."
199+
loading={isSubmitting}
200+
disabled={isSubmitting}
201+
value={inputValue}
202+
onChange={setInputValue}
203+
isLoggedIn={!!user}
204+
autoFocus={false}
205+
appType={appType}
206+
onAppTypeChange={setAppType}
207+
selectedModel={selectedModel}
208+
onModelChange={setSelectedModel}
209+
bgColor="bg-[#121212]"
210+
variant="home"
211+
/>
213212
{/* Grid line extension effect */}
214213
<div className="absolute -left-4 top-1/2 w-4 h-px bg-zinc-800/50" />
215214
<div className="absolute -right-4 top-1/2 w-4 h-px bg-zinc-800/50" />

frontend/src/components/suggestions/examples.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const Examples = ({
9090
{allPrompts.map((prompt, index) => (
9191
<motion.div
9292
key={`${prompt.title}-${index}`}
93-
initial={{ opacity: 0, scale: 0.8 }}
93+
initial={{ opacity: 0, scale: 0.95 }}
9494
animate={{ opacity: 1, scale: 1 }}
9595
transition={{
9696
duration: 0.3,
@@ -99,14 +99,14 @@ export const Examples = ({
9999
}}
100100
>
101101
<button
102-
className="w-fit h-fit px-3 py-2 rounded-full border border-zinc-800 bg-black hover:bg-zinc-900 hover:border-zinc-700 text-sm font-normal text-muted-foreground hover:text-white transition-all cursor-pointer"
102+
className="w-fit h-fit px-4 py-2.5 rounded-none border border-white/5 bg-[#09090b] shadow-[0_1px_2px_rgba(0,0,0,0.5),inset_0_1px_0_rgba(255,255,255,0.05)] hover:bg-[#121212] hover:border-white/10 text-[11px] font-mono font-medium tracking-wider text-zinc-400 hover:text-white transition-all cursor-pointer group"
103103
onClick={() => onSelectPrompt && onSelectPrompt(prompt.query)}
104104
>
105-
<div className="flex items-center gap-2">
106-
<div className="flex-shrink-0">
105+
<div className="flex items-center gap-3">
106+
<div className="flex-shrink-0 opacity-50 group-hover:opacity-100 transition-all duration-300 grayscale group-hover:grayscale-0 scale-90 group-hover:scale-100">
107107
{prompt.icon}
108108
</div>
109-
<span className="whitespace-nowrap">{prompt.title}</span>
109+
<span className="whitespace-nowrap uppercase">{prompt.title}</span>
110110
</div>
111111
</button>
112112
</motion.div>

frontend/src/components/thread/chat-input/chat-input.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export interface ChatInputProps {
5050
// Model selection
5151
selectedModel?: string;
5252
onModelChange?: (modelId: string) => void;
53+
variant?: 'default' | 'home';
5354
}
5455

5556
export interface UploadedFile {
@@ -78,13 +79,14 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
7879
onAgentSelect,
7980
// Removed unused agentName prop - custom agents no longer supported
8081
messages = [],
81-
bgColor = 'bg-card',
82+
bgColor = 'bg-[#121212]',
8283
isLoggedIn = true,
8384
disableAnimation = false,
8485
appType = 'web',
8586
onAppTypeChange,
8687
selectedModel,
8788
onModelChange,
89+
variant = 'default',
8890
},
8991
ref,
9092
) => {
@@ -244,18 +246,21 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
244246
>
245247
<div className="w-full text-sm flex flex-col justify-between items-start">
246248
<div className={cn(
247-
"w-full p-2 transition-colors duration-200 rounded-2xl overflow-hidden",
249+
"w-full transition-colors duration-200 overflow-hidden",
250+
variant === 'home' ? "rounded-none border border-white/5 shadow-[0_20px_50px_-12px_rgba(0,0,0,1),inset_0_1px_0_rgba(255,255,255,0.15)] bg-[#09090b]" : "rounded-2xl",
248251
bgColor,
249252
isDraggingOver && "bg-zinc-900"
250253
)}>
251-
<AttachmentGroup
252-
files={uploadedFiles || []}
253-
sandboxId={sandboxId}
254-
onRemove={removeUploadedFile}
255-
layout="inline"
256-
maxHeight="216px"
257-
showPreviews={true}
258-
/>
254+
<div className={cn(variant === 'home' ? "px-4 pt-2" : "px-2 pt-2")}>
255+
<AttachmentGroup
256+
files={uploadedFiles || []}
257+
sandboxId={sandboxId}
258+
onRemove={removeUploadedFile}
259+
layout="inline"
260+
maxHeight="216px"
261+
showPreviews={true}
262+
/>
263+
</div>
259264
<MessageInput
260265
ref={textareaRef}
261266
value={value}
@@ -287,6 +292,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
287292
onAppTypeChange={onAppTypeChange}
288293
selectedModel={selectedModel}
289294
onModelChange={onModelChange}
295+
variant={variant}
290296
/>
291297
</div>
292298
</div>

frontend/src/components/thread/chat-input/file-upload-handler.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Paperclip, Loader2 } from 'lucide-react';
66
import { toast } from 'sonner';
77
import { useAuth } from '@clerk/nextjs';
88
import { useQueryClient } from '@tanstack/react-query';
9+
import { cn } from '@/lib/utils';
910
import { fileQueryKeys } from '@/hooks/react-query/files/use-file-queries';
1011
// Tooltip removed to prevent ref compose loops
1112
import { UploadedFile } from './chat-input';
@@ -201,6 +202,7 @@ interface FileUploadHandlerProps {
201202
messages?: any[]; // Add messages prop
202203
isLoggedIn?: boolean;
203204
appType?: 'web' | 'mobile';
205+
className?: string;
204206
}
205207

206208
export const FileUploadHandler = forwardRef<
@@ -220,6 +222,7 @@ export const FileUploadHandler = forwardRef<
220222
messages = [],
221223
isLoggedIn = true,
222224
appType = 'web',
225+
className,
223226
},
224227
ref,
225228
) => {
@@ -277,15 +280,18 @@ export const FileUploadHandler = forwardRef<
277280
onClick={handleFileUpload}
278281
variant="ghost"
279282
size="sm"
280-
className="h-8 w-8 p-0 bg-transparent hover:bg-zinc-800/50 rounded-full text-zinc-400 hover:text-zinc-200 flex items-center justify-center transition-colors"
283+
className={cn(
284+
"h-8 w-8 p-0 bg-transparent hover:bg-zinc-800/50 rounded-full text-zinc-400 hover:text-zinc-200 flex items-center justify-center transition-colors",
285+
className
286+
)}
281287
disabled={
282288
!isLoggedIn || loading || (disabled && !isAgentRunning) || isUploading
283289
}
284290
>
285291
{isUploading ? (
286292
<Loader2 className="h-4 w-4 animate-spin" />
287293
) : (
288-
<Paperclip className="h-4 w-4" />
294+
<Paperclip className={cn("h-4 w-4", className?.includes("h-9") && "h-5 w-5")} />
289295
)}
290296
</Button>
291297
</span>

0 commit comments

Comments
 (0)