Skip to content

Commit 0fdf79a

Browse files
committed
wip: deploy dialogs
1 parent 9c593ec commit 0fdf79a

File tree

17 files changed

+207
-148
lines changed

17 files changed

+207
-148
lines changed

apps/deploy-worker/.env.example

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ SUPABASE_ANON_KEY="<SUPABASE_ANON_KEY>"
22
SUPABASE_OAUTH_CLIENT_ID="<SUPABASE_OAUTH_CLIENT_ID>"
33
SUPABASE_OAUTH_SECRET="<SUPABASE_OAUTH_SECRET>"
44
SUPABASE_SERVICE_ROLE_KEY="<SUPABASE_SERVICE_ROLE_KEY>"
5-
SUPABASE_URL="<SUPABASE_URL>"
5+
SUPABASE_URL="<SUPABASE_URL>"
6+
SUPABASE_PLATFORM_URL="https://supabase.com"
7+
SUPABASE_PLATFORM_API_URL="https://api.supabase.com"
8+
SUPABASE_PLATFORM_DEPLOY_REGION="us-east-1"

apps/deploy-worker/src/supabase/create-deployed-database.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DeployError } from '../error.ts'
22
import { generatePassword } from './generate-password.ts'
33
import { getAccessToken } from './get-access-token.ts'
44
import { createManagementApiClient } from './management-api/client.ts'
5-
import type { SupabaseClient, SupabaseProviderMetadata } from './types.ts'
5+
import type { Region, SupabaseClient, SupabaseProviderMetadata } from './types.ts'
66
import { waitForDatabaseToBeHealthy, waitForProjectToBeHealthy } from './wait-for-health.ts'
77

88
/**
@@ -74,7 +74,7 @@ export async function createDeployedDatabase(
7474
db_pass: databasePassword,
7575
name: `database-build-${params.databaseId}`,
7676
organization_id: (integration.data.scope as { organizationId: string }).organizationId,
77-
region: 'us-east-1',
77+
region: process.env.SUPABASE_PLATFORM_DEPLOY_REGION as Region,
7878
},
7979
}
8080
)

apps/deploy-worker/src/supabase/deploy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export async function deploy(
186186

187187
return {
188188
name: project.name,
189-
url: `https://supabase.com/dashboard/project/${project.id}`,
189+
url: `${process.env.SUPABASE_PLATFORM_URL}/dashboard/project/${project.id}`,
190190
databasePassword,
191191
databaseUrl: getDatabaseUrl({ project, databasePassword }),
192192
poolerUrl: getPoolerUrl({ project, databasePassword }),

apps/deploy-worker/src/supabase/get-access-token.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,21 @@ export async function getAccessToken(params: {
2525
if (new Date(credentials.expiresAt) < new Date(Date.now() + 1 * 60 * 60 * 1000)) {
2626
const now = Date.now()
2727

28-
const newCredentialsResponse = await fetch('https://api.supabase.com/v1/oauth/token', {
29-
method: 'POST',
30-
headers: {
31-
'Content-Type': 'application/x-www-form-urlencoded',
32-
Accept: 'application/json',
33-
Authorization: `Basic ${btoa(`${process.env.SUPABASE_OAUTH_CLIENT_ID}:${process.env.SUPABASE_OAUTH_SECRET}`)}`,
34-
},
35-
body: new URLSearchParams({
36-
grant_type: 'refresh_token',
37-
refresh_token: credentials.refreshToken,
38-
}),
39-
})
28+
const newCredentialsResponse = await fetch(
29+
`${process.env.SUPABASE_PLATFORM_API_URL}/v1/oauth/token`,
30+
{
31+
method: 'POST',
32+
headers: {
33+
'Content-Type': 'application/x-www-form-urlencoded',
34+
Accept: 'application/json',
35+
Authorization: `Basic ${btoa(`${process.env.SUPABASE_OAUTH_CLIENT_ID}:${process.env.SUPABASE_OAUTH_SECRET}`)}`,
36+
},
37+
body: new URLSearchParams({
38+
grant_type: 'refresh_token',
39+
refresh_token: credentials.refreshToken,
40+
}),
41+
}
42+
)
4043

4144
if (!newCredentialsResponse.ok) {
4245
if (newCredentialsResponse.status === 406) {

apps/deploy-worker/src/supabase/management-api/client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const integrationRevokedMiddleware: Middleware = {
1212

1313
export function createManagementApiClient(accessToken: string) {
1414
const client = createClient<paths>({
15-
baseUrl: 'https://api.supabase.com/',
15+
baseUrl: process.env.SUPABASE_PLATFORM_API_URL,
1616
headers: {
1717
'Content-Type': 'application/json',
1818
Authorization: `Bearer ${accessToken}`,

apps/deploy-worker/src/supabase/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ type Database = Unpacked<
1313
paths['/v1/projects/{ref}/config/database/pooler']['get']['responses']['200']['content']['application/json']
1414
>
1515

16+
export type Region =
17+
paths['/v1/projects']['post']['requestBody']['content']['application/json']['region']
18+
1619
export type SupabaseProviderMetadata = {
1720
project: {
1821
id: Project['id']

apps/postgres-new/.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ NEXT_PUBLIC_SUPABASE_URL="<supabase-api-url>"
33
NEXT_PUBLIC_BROWSER_PROXY_DOMAIN="<browser-proxy-domain>"
44
NEXT_PUBLIC_DEPLOY_WORKER_DOMAIN="<deploy-worker-domain>"
55
NEXT_PUBLIC_SUPABASE_OAUTH_CLIENT_ID="<supabase-oauth-client-id>"
6+
NEXT_PUBLIC_SUPABASE_PLATFORM_API_URL=https://api.supabase.com
67

78
OPENAI_API_KEY="<openai-api-key>"
89
# Optional

apps/postgres-new/app/(main)/db/[id]/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import { useRouter } from 'next/navigation'
44
import { useEffect } from 'react'
55
import { useApp } from '~/components/app-provider'
6-
import { DeployFailureDialog } from '~/components/deploy-failure-dialog'
7-
import { DeploySuccessDialog } from '~/components/deploy-success-dialog'
6+
import { DeployFailureDialog } from '~/components/deploy/deploy-failure-dialog'
7+
import { DeploySuccessDialog } from '~/components/deploy/deploy-success-dialog'
88
import Workspace from '~/components/workspace'
99

1010
export default function Page({ params }: { params: { id: string } }) {

apps/postgres-new/app/api/oauth/supabase/callback/route.ts

+28-20
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,22 @@ export async function GET(req: NextRequest) {
4545
const now = Date.now()
4646

4747
// get tokens
48-
const tokensResponse = await fetch('https://api.supabase.com/v1/oauth/token', {
49-
method: 'POST',
50-
headers: {
51-
'Content-Type': 'application/x-www-form-urlencoded',
52-
Accept: 'application/json',
53-
Authorization: `Basic ${btoa(`${process.env.NEXT_PUBLIC_SUPABASE_OAUTH_CLIENT_ID}:${process.env.SUPABASE_OAUTH_SECRET}`)}`,
54-
},
55-
body: new URLSearchParams({
56-
grant_type: 'authorization_code',
57-
code,
58-
redirect_uri: req.nextUrl.origin + '/api/oauth/supabase/callback',
59-
}),
60-
})
48+
const tokensResponse = await fetch(
49+
`${process.env.NEXT_PUBLIC_SUPABASE_PLATFORM_API_URL}/v1/oauth/token`,
50+
{
51+
method: 'POST',
52+
headers: {
53+
'Content-Type': 'application/x-www-form-urlencoded',
54+
Accept: 'application/json',
55+
Authorization: `Basic ${btoa(`${process.env.NEXT_PUBLIC_SUPABASE_OAUTH_CLIENT_ID}:${process.env.SUPABASE_OAUTH_SECRET}`)}`,
56+
},
57+
body: new URLSearchParams({
58+
grant_type: 'authorization_code',
59+
code,
60+
redirect_uri: req.nextUrl.origin + '/api/oauth/supabase/callback',
61+
}),
62+
}
63+
)
6164

6265
if (!tokensResponse.ok) {
6366
return new Response('Failed to get tokens', { status: 500 })
@@ -71,13 +74,18 @@ export async function GET(req: NextRequest) {
7174
token_type: 'Bearer'
7275
}
7376

74-
const organizationsResponse = await fetch('https://api.supabase.com/v1/organizations', {
75-
method: 'GET',
76-
headers: {
77-
Accept: 'application/json',
78-
Authorization: `Bearer ${tokens.access_token}`,
79-
},
80-
})
77+
console.log({ tokens })
78+
79+
const organizationsResponse = await fetch(
80+
`${process.env.NEXT_PUBLIC_SUPABASE_PLATFORM_API_URL}/v1/organizations`,
81+
{
82+
method: 'GET',
83+
headers: {
84+
Accept: 'application/json',
85+
Authorization: `Bearer ${tokens.access_token}`,
86+
},
87+
}
88+
)
8189

8290
if (!organizationsResponse.ok) {
8391
return new Response('Failed to get organizations', { status: 500 })
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use client'
2+
3+
import { DialogProps } from '@radix-ui/react-dialog'
4+
import { Button } from '~/components/ui/button'
5+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '~/components/ui/dialog'
6+
7+
export type DeployDialogProps = DialogProps & {
8+
onConfirm?: () => void
9+
}
10+
11+
export function DeployDialog({ onConfirm, ...props }: DeployDialogProps) {
12+
return (
13+
<Dialog {...props}>
14+
<DialogContent className="max-w-3xl">
15+
<DialogHeader>
16+
<DialogTitle>Deploy to Supabase</DialogTitle>
17+
<div className="py-2 border-b" />
18+
</DialogHeader>
19+
<div className="flex flex-col gap-6">
20+
<Button onClick={onConfirm}>Deploy</Button>
21+
</div>
22+
</DialogContent>
23+
</Dialog>
24+
)
25+
}

apps/postgres-new/components/deploy-failure-dialog.tsx renamed to apps/postgres-new/components/deploy/deploy-failure-dialog.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { Dialog, DialogContent, DialogTitle, DialogHeader } from './ui/dialog'
3+
import { Dialog, DialogContent, DialogTitle, DialogHeader } from '~/components/ui/dialog'
44
import { useRouter } from 'next/navigation'
55
import { useEffect, useState } from 'react'
66

apps/postgres-new/components/deploy-success-dialog.tsx renamed to apps/postgres-new/components/deploy/deploy-success-dialog.tsx

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use client'
22

3-
import { Dialog, DialogContent, DialogTitle, DialogHeader } from './ui/dialog'
4-
import { useRouter } from 'next/navigation'
5-
import { CopyableField } from './copyable-field'
63
import Link from 'next/link'
4+
import { useRouter } from 'next/navigation'
75
import { useEffect, useState } from 'react'
8-
import { Badge } from './ui/badge'
6+
import { CopyableField } from '~/components/copyable-field'
7+
import { Badge } from '~/components/ui/badge'
8+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '~/components/ui/dialog'
99

1010
export function DeploySuccessDialog() {
1111
const router = useRouter()
@@ -84,12 +84,12 @@ export function DeploySuccessDialog() {
8484
<>
8585
<CopyableField label="Database Password" value={project.databasePassword} />
8686
<span className="text-muted-foreground text-sm">
87+
{/* eslint-disable-next-line react/no-unescaped-entities */}
8788
Please{' '}
8889
<span className="text-foreground font-semibold">
8990
save your database password securely
9091
</span>{' '}
91-
{/* eslint-disable-next-line react/no-unescaped-entities */}
92-
as it won't be displayed again.
92+
as it won&apos;t be displayed again.
9393
</span>
9494
</>
9595
) : null}
@@ -103,6 +103,7 @@ export function DeploySuccessDialog() {
103103
>
104104
database settings
105105
</Link>
106+
.
106107
</span>
107108
</p>
108109
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use client'
2+
3+
import { DialogProps } from '@radix-ui/react-dialog'
4+
import { Button } from '~/components/ui/button'
5+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '~/components/ui/dialog'
6+
7+
export type IntegrationDialogProps = DialogProps & {
8+
onConfirm?: () => void
9+
}
10+
11+
export function IntegrationDialog({ onConfirm, ...props }: IntegrationDialogProps) {
12+
return (
13+
<Dialog {...props}>
14+
<DialogContent className="max-w-3xl">
15+
<DialogHeader>
16+
<DialogTitle>Connect Supabase</DialogTitle>
17+
<div className="py-2 border-b" />
18+
</DialogHeader>
19+
<div className="flex flex-col gap-6">
20+
<p>
21+
To deploy your database, you need to connect your Supabase account. If you don&apos;t
22+
already have a Supabase account, you can create one for free.
23+
</p>
24+
<p>
25+
Click <strong>Connect</strong> to connect your account.
26+
</p>
27+
<Button onClick={onConfirm}>Connect</Button>
28+
</div>
29+
</DialogContent>
30+
</Dialog>
31+
)
32+
}

0 commit comments

Comments
 (0)