diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx index ac0ba0ffa..4224bf658 100644 --- a/apps/web/src/app/(dashboard)/dashboard/page.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/page.tsx @@ -87,9 +87,9 @@ const Dashboard = ({ user }) => { const [teams, setTeams] = useState([]) const [currentTeam, setCurrentTeam] = useState(null) - const apiUrlState = useLocalStorage( - 'apiUrl', - process.env.NEXT_PUBLIC_API_URL || '' + const domainState = useLocalStorage( + 'e2bDomain', + process.env.NEXT_PUBLIC_DOMAIN || '' ) const initialTab = @@ -162,7 +162,7 @@ const Dashboard = ({ user }) => { teams={teams} setTeams={setTeams} setCurrentTeam={setCurrentTeam} - apiUrlState={apiUrlState} + domainState={domainState} /> @@ -250,7 +250,7 @@ function MainContent({ teams, setTeams, setCurrentTeam, - apiUrlState, + domainState, }: { selectedItem: MenuLabel user: E2BUser @@ -258,29 +258,29 @@ function MainContent({ teams: Team[] setTeams: (teams: Team[]) => void setCurrentTeam: (team: Team) => void - apiUrlState: [string, (value: string) => void] + domainState: [string, (value: string) => void] }) { switch (selectedItem) { case 'personal': - return + return case 'keys': return ( - + ) case 'sandboxes': - return + return case 'templates': return ( ) case 'usage': - return + return case 'billing': - return + return case 'team': return ( ) case 'developer': - return + return default: return } diff --git a/apps/web/src/app/(dashboard)/dashboard/utils.ts b/apps/web/src/app/(dashboard)/dashboard/utils.ts new file mode 100644 index 000000000..0b8e81e95 --- /dev/null +++ b/apps/web/src/app/(dashboard)/dashboard/utils.ts @@ -0,0 +1,27 @@ +export function getBaseUrl(domain: string) { + let url = domain + if (!domain.startsWith('http')) { + const local = domain === 'localhost' || domain.startsWith('127.0.0.') + url = `http${local ? '' : 's'}://${domain}` + } + + const parsedUrl = new URL(url) + + return parsedUrl.toString() +} + +export function getBillingUrl(domain: string) { + let url = domain + const local = domain === 'localhost' || domain.startsWith('127.0.0.') + + if (!domain.startsWith('http')) { + url = `http${local ? '' : 's'}://${domain}` + } + + const parsedUrl = new URL(url) + if (!local) { + parsedUrl.hostname = `billing.${parsedUrl.hostname}` + } + + return parsedUrl.toString() +} diff --git a/apps/web/src/components/Dashboard/Billing.tsx b/apps/web/src/components/Dashboard/Billing.tsx index 97e7c3828..8054794fa 100644 --- a/apps/web/src/components/Dashboard/Billing.tsx +++ b/apps/web/src/components/Dashboard/Billing.tsx @@ -12,6 +12,7 @@ import { } from '../ui/table' import SwitchToHobbyButton from '@/components/Pricing/SwitchToHobbyButton' import SwitchToProButton from '@/components/Pricing/SwitchToProButton' +import { getBillingUrl } from '@/app/(dashboard)/dashboard/utils' function formatCurrency(value: number) { return value.toLocaleString('en-US', { @@ -29,10 +30,10 @@ interface Invoice { export const BillingContent = ({ team, - apiUrl, + domain, }: { team: Team - apiUrl: string + domain: string }) => { const [invoices, setInvoices] = useState([]) const [credits, setCredits] = useState(null) @@ -41,7 +42,7 @@ export const BillingContent = ({ const getInvoices = async function getInvoices() { setInvoices([]) const res = await fetch( - `https://billing.${apiUrl}/teams/${team.id}/invoices`, + `https://billing.${domain}/teams/${team.id}/invoices`, { headers: { 'X-Team-API-Key': team.apiKeys[0], @@ -59,7 +60,7 @@ export const BillingContent = ({ setCredits(null) const creditsRes = await fetch( - `https://billing.${apiUrl}/teams/${team.id}/usage`, + `${getBillingUrl(domain)}/teams/${team.id}/usage`, { headers: { 'X-Team-API-Key': team.apiKeys[0], @@ -71,7 +72,7 @@ export const BillingContent = ({ } getInvoices() - }, [team]) + }, [domain, team]) return (
@@ -114,7 +115,7 @@ export const BillingContent = ({

Pro tier

- +
  • One-time $100 credits
  • diff --git a/apps/web/src/components/Dashboard/Keys.tsx b/apps/web/src/components/Dashboard/Keys.tsx index e17b36549..b4fa43ce7 100644 --- a/apps/web/src/components/Dashboard/Keys.tsx +++ b/apps/web/src/components/Dashboard/Keys.tsx @@ -28,6 +28,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu' +import { getBillingUrl } from '@/app/(dashboard)/dashboard/utils' type TeamApiKey = { id: string @@ -46,11 +47,11 @@ type TeamApiKey = { export const KeysContent = ({ currentTeam, user, - apiUrl, + domain, }: { currentTeam: Team user: E2BUser - apiUrl: string + domain: string }) => { const { toast } = useToast() const [isKeyDialogOpen, setIsKeyDialogOpen] = useState(false) @@ -60,11 +61,10 @@ export const KeysContent = ({ const [newApiKeyInput, setNewApiKeyInput] = useState('') const [apiKeys, setApiKeys] = useState([]) - console.log(apiUrl) useEffect(() => { async function fetchApiKeys() { const res = await fetch( - `https://billing.${apiUrl}/teams/${currentTeam.id}/api-keys`, + `${getBillingUrl(domain)}/teams/${currentTeam.id}/api-keys`, { headers: { 'X-USER-ACCESS-TOKEN': user.accessToken, @@ -86,7 +86,7 @@ export const KeysContent = ({ } fetchApiKeys() - }, [apiUrl, currentTeam]) + }, [domain, currentTeam, user.accessToken]) async function deleteApiKey() { if (apiKeys.length === 1) { @@ -98,7 +98,9 @@ export const KeysContent = ({ } const res = await fetch( - `https://billing.${apiUrl}/teams/${currentTeam.id}/api-keys/${currentKey?.id}`, + `${getBillingUrl(domain)}/teams/${currentTeam.id}/api-keys/${ + currentKey?.id + }`, { method: 'DELETE', headers: { @@ -121,7 +123,7 @@ export const KeysContent = ({ async function createApiKey() { const res = await fetch( - `https://billing.${apiUrl}/teams/${currentTeam.id}/api-keys`, + `${getBillingUrl(domain)}/teams/${currentTeam.id}/api-keys`, { method: 'POST', headers: { @@ -153,7 +155,9 @@ export const KeysContent = ({ async function updateApiKey() { const res = await fetch( - `https://billing.${apiUrl}/teams/${currentTeam.id}/api-keys/${currentKey?.id}`, + `${getBillingUrl(domain)}/teams/${currentTeam.id}/api-keys/${ + currentKey?.id + }`, { method: 'PATCH', headers: { diff --git a/apps/web/src/components/Dashboard/Personal.tsx b/apps/web/src/components/Dashboard/Personal.tsx index 10937ae31..85b4c5b66 100644 --- a/apps/web/src/components/Dashboard/Personal.tsx +++ b/apps/web/src/components/Dashboard/Personal.tsx @@ -6,13 +6,14 @@ import Link from 'next/link' import { useState } from 'react' import { Copy } from 'lucide-react' import { E2BUser } from '@/utils/useUser' +import { getBillingUrl } from '@/app/(dashboard)/dashboard/utils' export const PersonalContent = ({ user, - apiUrl, + domain, }: { user: E2BUser - apiUrl: string + domain: string }) => { const { toast } = useToast() const [hovered, setHovered] = useState(false) @@ -33,7 +34,7 @@ export const PersonalContent = ({ } const updateUserEmail = async () => { - const res = await fetch(`https://billing.${apiUrl}/users`, { + const res = await fetch(`${getBillingUrl(domain)}/users`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', diff --git a/apps/web/src/components/Dashboard/Sandboxes.tsx b/apps/web/src/components/Dashboard/Sandboxes.tsx index 0a5e7b90e..50569fc84 100644 --- a/apps/web/src/components/Dashboard/Sandboxes.tsx +++ b/apps/web/src/components/Dashboard/Sandboxes.tsx @@ -10,6 +10,7 @@ import { import { useState } from 'react' import { useEffect } from 'react' import { Team } from '@/utils/useUser' +import { getBaseUrl } from '@/app/(dashboard)/dashboard/utils' interface Sandbox { alias: string @@ -25,10 +26,10 @@ interface Sandbox { export function SandboxesContent({ team, - apiUrl, + domain, }: { team: Team - apiUrl: string + domain: string }) { const [runningSandboxes, setRunningSandboxes] = useState([]) @@ -36,7 +37,7 @@ export function SandboxesContent({ function f() { const apiKey = team.apiKeys[0] if (apiKey) { - fetchSandboxes(apiUrl, apiKey).then((newSandboxes) => { + fetchSandboxes(domain, apiKey).then((newSandboxes) => { if (newSandboxes) { setRunningSandboxes(newSandboxes) } @@ -101,10 +102,10 @@ export function SandboxesContent({ } async function fetchSandboxes( - apiUrl: string, + domain: string, apiKey: string ): Promise { - const res = await fetch(`${apiUrl}/sandboxes`, { + const res = await fetch(`${getBaseUrl(domain)}/sandboxes`, { method: 'GET', headers: { 'X-API-KEY': apiKey, diff --git a/apps/web/src/components/Dashboard/Team.tsx b/apps/web/src/components/Dashboard/Team.tsx index 574d55085..0171c72da 100644 --- a/apps/web/src/components/Dashboard/Team.tsx +++ b/apps/web/src/components/Dashboard/Team.tsx @@ -24,6 +24,7 @@ import { AlertDialogTitle, } from '../ui/alert-dialog' import Spinner from '@/components/Spinner' +import { getBillingUrl } from '@/app/(dashboard)/dashboard/utils' interface TeamMember { id: string @@ -45,14 +46,14 @@ export const TeamContent = ({ teams, setTeams, setCurrentTeam, - apiUrl, + domain, }: { team: Team user: E2BUser teams: Team[] setTeams: (teams: Team[]) => void setCurrentTeam: (team: Team) => void - apiUrl: string + domain: string }) => { const [isDialogOpen, setIsDialogOpen] = useState(false) const [currentMemberId, setCurrentMemberId] = useState(null) @@ -65,7 +66,7 @@ export const TeamContent = ({ useEffect(() => { const getTeamMembers = async () => { const res = await fetch( - `https://billing.${apiUrl}/teams/${team.id}/users`, + `${getBillingUrl(domain)}/teams/${team.id}/users`, { headers: { 'X-User-Access-Token': user.accessToken, @@ -91,7 +92,7 @@ export const TeamContent = ({ } getTeamMembers() - }, [user, userAdded, team]) + }, [user, userAdded, team, domain]) useEffect(() => { setTeamName(team.name) @@ -104,17 +105,14 @@ export const TeamContent = ({ } const deleteUserFromTeam = async () => { - const res = await fetch( - `https://billing.${apiUrl}/teams/${team.id}/users`, - { - method: 'DELETE', - headers: { - 'X-User-Access-Token': user.accessToken, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ user_id: currentMemberId }), - } - ) + const res = await fetch(`${getBillingUrl(domain)}/teams/${team.id}/users`, { + method: 'DELETE', + headers: { + 'X-User-Access-Token': user.accessToken, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ user_id: currentMemberId }), + }) if (!res.ok) { toast({ @@ -130,7 +128,7 @@ export const TeamContent = ({ } const changeTeamName = async () => { - const res = await fetch(`https://billing.${apiUrl}/teams/${team.id}`, { + const res = await fetch(`${getBillingUrl(domain)}/teams/${team.id}`, { headers: { 'X-Team-API-Key': team.apiKeys[0], 'Content-Type': 'application/json', @@ -167,17 +165,14 @@ export const TeamContent = ({ return } - const res = await fetch( - `https://billing.${apiUrl}/teams/${team.id}/users`, - { - headers: { - 'X-User-Access-Token': user.accessToken, - 'Content-Type': 'application/json', - }, - method: 'POST', - body: JSON.stringify({ user_email: userToAdd.trim() }), - } - ) + const res = await fetch(`${getBillingUrl(domain)}/teams/${team.id}/users`, { + headers: { + 'X-User-Access-Token': user.accessToken, + 'Content-Type': 'application/json', + }, + method: 'POST', + body: JSON.stringify({ user_email: userToAdd.trim() }), + }) if (!res.ok) { toast({ diff --git a/apps/web/src/components/Dashboard/Templates.tsx b/apps/web/src/components/Dashboard/Templates.tsx index 939df885c..a1bcdd573 100644 --- a/apps/web/src/components/Dashboard/Templates.tsx +++ b/apps/web/src/components/Dashboard/Templates.tsx @@ -28,6 +28,7 @@ import { AlertDialogAction, } from '../ui/alert-dialog' import { toast } from '../ui/use-toast' +import { getBaseUrl } from '@/app/(dashboard)/dashboard/utils' interface Template { aliases: string[] @@ -47,11 +48,11 @@ interface Template { export function TemplatesContent({ user, teamId, - apiUrl, + domain, }: { user: E2BUser teamId: string - apiUrl: string + domain: string }) { const [templates, setTemplates] = useState([]) const [currentTemplate, setCurrentTemplate] = useState