diff --git a/studio/data/api.d.ts b/studio/data/api.d.ts index f799582b862..ca72c8adcfc 100644 --- a/studio/data/api.d.ts +++ b/studio/data/api.d.ts @@ -133,9 +133,13 @@ export interface paths { '/platform/organizations': { /** Gets user's organizations */ get: operations['OrganizationsController_getOrganizations'] - /** Creates an organization (v2) */ + /** Creates an organization */ post: operations['OrganizationsController_createOrganizationWithTier'] } + '/platform/organizations/fly/{fly_organization_id}': { + /** Gets organization linked to fly organization id */ + get: operations['OrganizationsController_getOrganizationByFlyOrganizationId'] + } '/platform/organizations/{slug}': { /** Deletes organization */ delete: operations['OrganizationSlugController_deleteOrganization'] @@ -443,6 +447,10 @@ export interface paths { /** Creates a project */ post: operations['ProjectsController_createProject'] } + '/platform/projects/fly/{fly_extension_id}': { + /** Gets project linked to fly extension id */ + get: operations['ProjectsController_getProjectByFlyExtensionId'] + } '/platform/projects/{ref}/content': { /** Gets project's content */ get: operations['ContentController_getContent'] @@ -529,10 +537,6 @@ export interface paths { */ post: operations['UpdateController_updateProject'] } - '/platform/projects/{ref}/usage': { - /** Gets project's usage */ - get: operations['UsageController_getUsageStatusConfig'] - } '/platform/projects/{ref}/transfer/preview': { /** Previews transfering a project to a different organizations, shows eligibility and impact. */ post: operations['ProjectTransferController_previewTransfer'] @@ -595,20 +599,6 @@ export interface paths { /** Removes project addon */ delete: operations['ProjectAddonController_removeAddon'] } - '/platform/projects/{ref}/billing/subscription': { - /** Gets the current subscription */ - get: operations['SubscriptionController_getSubscription'] - /** Updates subscription */ - put: operations['SubscriptionController_updateSubscription'] - } - '/platform/projects/{ref}/billing/plans': { - /** Gets subscription plans */ - get: operations['ProjectPlansController_getAvailablePlans'] - } - '/platform/projects/{ref}/billing/invoices/upcoming': { - /** Gets the upcoming invoice */ - get: operations['ProjectInvoicesController_getUpcomingInvoice'] - } '/platform/props/project/{ref}/api': { /** * Gets project's api info @@ -876,10 +866,6 @@ export interface paths { /** Allows a project to obtain temporary credentials. */ post: operations['AwsCredentialsController_getTemporaryCredentials'] } - '/system/projects/{ref}/billing/subscription': { - /** Updates subscription */ - put: operations['SubscriptionController_updateSubscription'] - } '/system/projects/{ref}/billing/addons': { /** Updates project addon */ post: operations['AddonsController_updateAddon'] @@ -888,18 +874,6 @@ export interface paths { /** Removes project addon */ delete: operations['AddonsController_removeAddon'] } - '/system/billing/migrate/org-level-billing': { - /** Migrates org to org-level billing. */ - post: operations['BillingMigrationController_migrateToOrgLevelBilling'] - } - '/system/billing/migrate/org-level-billing-preview': { - /** Previews the migration of the organization to the new org level billing. */ - post: operations['BillingMigrationController_preview'] - } - '/system/billing/migrate/org-level-billing-attach': { - /** Attaches subscription id to org and projects. */ - put: operations['BillingMigrationController_attachSubscriptionId'] - } '/system/projects/{ref}/config/update-jwt/complete': { /** Handle update project jwt on completion */ post: operations['ProjectUpdateJwtController_completeUpdateJwt'] @@ -1013,9 +987,13 @@ export interface paths { '/v0/organizations': { /** Gets user's organizations */ get: operations['OrganizationsController_getOrganizations'] - /** Creates an organization (v2) */ + /** Creates an organization */ post: operations['OrganizationsController_createOrganizationWithTier'] } + '/v0/organizations/fly/{fly_organization_id}': { + /** Gets organization linked to fly organization id */ + get: operations['OrganizationsController_getOrganizationByFlyOrganizationId'] + } '/v0/organizations/{slug}': { /** Deletes organization */ delete: operations['OrganizationSlugController_deleteOrganization'] @@ -1199,6 +1177,10 @@ export interface paths { /** Creates a project */ post: operations['ProjectsController_createProject'] } + '/v0/projects/fly/{fly_extension_id}': { + /** Gets project linked to fly extension id */ + get: operations['ProjectsController_getProjectByFlyExtensionId'] + } '/v0/projects/metrics': { /** * Get metrics @@ -1285,10 +1267,6 @@ export interface paths { /** Gets project's status */ get: operations['StatusController_getStatus'] } - '/v0/projects/{ref}/usage': { - /** Gets project's usage */ - get: operations['UsageController_getUsageStatusConfig'] - } '/v0/projects/{ref}/analytics/endpoints/functions.inv-stats': { /** Gets a project's function invocation statistics */ get: operations['FunctionLogsController_getStatus'] @@ -1343,20 +1321,6 @@ export interface paths { /** Removes project addon */ delete: operations['ProjectAddonController_removeAddon'] } - '/v0/projects/{ref}/billing/subscription': { - /** Gets the current subscription */ - get: operations['SubscriptionController_getSubscription'] - /** Updates subscription */ - put: operations['SubscriptionController_updateSubscription'] - } - '/v0/projects/{ref}/billing/plans': { - /** Gets subscription plans */ - get: operations['ProjectPlansController_getAvailablePlans'] - } - '/v0/projects/{ref}/billing/invoices/upcoming': { - /** Gets the upcoming invoice */ - get: operations['ProjectInvoicesController_getUpcomingInvoice'] - } '/v0/storage/{ref}/buckets/{id}': { /** Gets bucket */ get: operations['StorageBucketIdController_getBucket'] @@ -2273,17 +2237,12 @@ export interface components { id: string name: string } + GetOrganizationByFlyOrganizationIdResponse: { + slug: string + } CreateOrganizationBody: { name: string } - CreateOrganizationBodyV2: { - name: string - kind?: string - size?: string - /** @enum {string} */ - tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise' - payment_method?: string - } UpdateOrganizationBody: { name: string billing_email: string @@ -3353,6 +3312,9 @@ export interface components { preview_branch_refs: string[] disk_volume_size_gb?: number } + GetProjectByFlyExtensionIdResponse: { + ref: string + } AmiSearchOptions: { search_tags?: Record } @@ -3367,7 +3329,8 @@ export interface components { /** @description Slug of your organization */ organization_id: string /** - * @description Subscription plan + * @deprecated + * @description Subscription plan is now set on organization level and is ignored in this request * @example free * @enum {string} */ @@ -3595,26 +3558,6 @@ export interface components { jwt_secret?: string service_api_keys?: components['schemas']['ProjectServiceApiKeyResponse'][] } - UsageStatus: { - usage: number - limit: number - cost: number - available_in_plan: boolean - } - UsageStatusResponse: { - db_size: components['schemas']['UsageStatus'] - storage_size: components['schemas']['UsageStatus'] - db_egress: components['schemas']['UsageStatus'] - storage_egress: components['schemas']['UsageStatus'] - storage_image_render_count: components['schemas']['UsageStatus'] - monthly_active_users: components['schemas']['UsageStatus'] - monthly_active_sso_users: components['schemas']['UsageStatus'] - func_invocations: components['schemas']['UsageStatus'] - func_count: components['schemas']['UsageStatus'] - realtime_message_count: components['schemas']['UsageStatus'] - realtime_peak_connection: components['schemas']['UsageStatus'] - disk_volume_size_gb: number - } TransferProjectBody: { target_organization_slug: string } @@ -3777,11 +3720,6 @@ export interface components { /** @enum {string} */ addon_type: 'custom_domain' | 'compute_instance' | 'pitr' } - UpdateSubscriptionV2Body: { - payment_method?: string - /** @enum {string} */ - tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise' - } ServiceApiKey: { api_key_encrypted?: string tags: string @@ -4266,41 +4204,24 @@ export interface components { /** Format: date-time */ expiry_time: string } - UpdateSubscriptionV2AdminBody: { - payment_method?: string - /** @enum {string} */ - tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise' - price_id?: string - } UpdateAddonAdminBody: { addon_variant: string /** @enum {string} */ addon_type: 'custom_domain' | 'compute_instance' | 'pitr' price_id?: string } - MigrateToOrgLevelBillingBody: { - org_slug: string - /** @enum {string} */ - tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise' - custom_usage_fees?: components['schemas']['BillingUsageBasedPrice'][] - tier_price_id?: string - compute_credits?: number - payment_method_id?: string - existing_org_subscription_id?: string - dryRun?: boolean - force?: boolean - billing_cycle_anchor?: string - } - AttachSubscriptionIdBody: { - org_slug: string - subscription_id: string - } DatabaseResponse: { /** @description Database host */ host: string /** @description Database version */ version: string } + UpdateSubscriptionV2AdminBody: { + payment_method?: string + /** @enum {string} */ + tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise' + price_id?: string + } GetMetricsBody: { /** @enum {string} */ metric: 'user_queries' @@ -4342,6 +4263,7 @@ export interface components { UpdateBranchBody: { branch_name?: string git_branch?: string + reset_on_push?: boolean } BranchResponse: { id: string @@ -4351,6 +4273,7 @@ export interface components { is_default: boolean git_branch?: string pr_number?: number + reset_on_push: boolean created_at: string updated_at: string } @@ -5537,11 +5460,11 @@ export interface operations { } } } - /** Creates an organization (v2) */ + /** Creates an organization */ OrganizationsController_createOrganizationWithTier: { requestBody: { content: { - 'application/json': components['schemas']['CreateOrganizationBodyV2'] + 'application/json': components['schemas']['CreateOrganizationBody'] } } responses: { @@ -5556,6 +5479,21 @@ export interface operations { } } } + /** Gets organization linked to fly organization id */ + OrganizationsController_getOrganizationByFlyOrganizationId: { + parameters: { + path: { + fly_organization_id: string + } + } + responses: { + 200: { + content: { + 'application/json': components['schemas']['GetOrganizationByFlyOrganizationIdResponse'] + } + } + } + } /** Deletes organization */ OrganizationSlugController_deleteOrganization: { parameters: { @@ -6192,8 +6130,8 @@ export interface operations { SubscriptionController_getSubscription: { parameters: { path: { - /** @description Project ref */ - ref: string + /** @description Organization slug */ + slug: string } } responses: { @@ -6215,13 +6153,13 @@ export interface operations { SubscriptionController_updateSubscription: { parameters: { path: { - /** @description Project ref */ - ref: string + /** @description Organization slug */ + slug: string } } requestBody: { content: { - 'application/json': components['schemas']['UpdateSubscriptionV2Body'] + 'application/json': components['schemas']['UpdateSubscriptionBody'] } } responses: { @@ -6231,7 +6169,7 @@ export interface operations { 403: { content: never } - /** @description Failed to update subscription */ + /** @description Failed to update subscription change */ 500: { content: never } @@ -8133,6 +8071,21 @@ export interface operations { } } } + /** Gets project linked to fly extension id */ + ProjectsController_getProjectByFlyExtensionId: { + parameters: { + path: { + fly_extension_id: string + } + } + responses: { + 200: { + content: { + 'application/json': components['schemas']['GetProjectByFlyExtensionIdResponse'] + } + } + } + } /** Gets project's content */ ContentController_getContent: { parameters: { @@ -8666,26 +8619,6 @@ export interface operations { } } } - /** Gets project's usage */ - UsageController_getUsageStatusConfig: { - parameters: { - path: { - /** @description Project ref */ - ref: string - } - } - responses: { - 200: { - content: { - 'application/json': components['schemas']['UsageStatusResponse'] - } - } - /** @description Failed to retrieve project's usage */ - 500: { - content: never - } - } - } /** Previews transfering a project to a different organizations, shows eligibility and impact. */ ProjectTransferController_previewTransfer: { parameters: { @@ -9127,52 +9060,6 @@ export interface operations { } } } - /** Gets subscription plans */ - ProjectPlansController_getAvailablePlans: { - parameters: { - path: { - /** @description Project ref */ - ref: string - } - } - responses: { - 200: { - content: { - 'application/json': Record - } - } - 403: { - content: never - } - /** @description Failed to get subscription plans */ - 500: { - content: never - } - } - } - /** Gets the upcoming invoice */ - ProjectInvoicesController_getUpcomingInvoice: { - parameters: { - path: { - /** @description Project ref */ - ref: string - } - } - responses: { - 200: { - content: { - 'application/json': Record - } - } - 403: { - content: never - } - /** @description Failed to retrieve upcoming invoice */ - 500: { - content: never - } - } - } /** * Gets project's api info * @deprecated @@ -10191,7 +10078,7 @@ export interface operations { GitHubPullRequestController_getPullRequestsByNumber: { parameters: { query: { - pr_number: number + pr_number: number[] } path: { organization_integration_id: string @@ -10237,25 +10124,6 @@ export interface operations { } } } - /** Create CLI login session */ - CliLoginController_createCliLoginSession: { - requestBody: { - content: { - 'application/json': components['schemas']['CreateCliLoginSessionBody'] - } - } - responses: { - /** @description Failed to create CLI login session */ - 500: never - } - } - /** Retrieve CLI login session */ - CliLoginController_getCliLoginSession: { - responses: { - /** @description Failed to retrieve CLI login session */ - 500: never - } - } /** Gets GoTrue template */ AuthTemplateController_getTemplate: { parameters: { @@ -10654,57 +10522,6 @@ export interface operations { } } } - /** Migrates org to org-level billing. */ - BillingMigrationController_migrateToOrgLevelBilling: { - requestBody: { - content: { - 'application/json': components['schemas']['MigrateToOrgLevelBillingBody'] - } - } - responses: { - 201: { - content: never - } - /** @description Failed to migrate org. */ - 500: { - content: never - } - } - } - /** Previews the migration of the organization to the new org level billing. */ - BillingMigrationController_preview: { - requestBody: { - content: { - 'application/json': components['schemas']['MigrateToOrgLevelBillingBody'] - } - } - responses: { - 201: { - content: never - } - /** @description Failed to preview org billing organization */ - 500: { - content: never - } - } - } - /** Attaches subscription id to org and projects. */ - BillingMigrationController_attachSubscriptionId: { - requestBody: { - content: { - 'application/json': components['schemas']['AttachSubscriptionIdBody'] - } - } - responses: { - 200: { - content: never - } - /** @description Failed to preview org billing organization */ - 500: { - content: never - } - } - } /** Handle update project jwt on completion */ ProjectUpdateJwtController_completeUpdateJwt: { parameters: { diff --git a/studio/data/organizations/organization-by-fly-organization-id-mutation.ts b/studio/data/organizations/organization-by-fly-organization-id-mutation.ts new file mode 100644 index 00000000000..e2e34c0fb97 --- /dev/null +++ b/studio/data/organizations/organization-by-fly-organization-id-mutation.ts @@ -0,0 +1,47 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query' + +import { get } from 'data/fetchers' +import toast from 'react-hot-toast' +import { ResponseError } from 'types' + +export type OrganizationByFlyOrgIdVariables = { + flyOrganizationId: string +} + +export async function getOrganizationByFlyOrgId({ + flyOrganizationId, +}: OrganizationByFlyOrgIdVariables) { + const { data, error } = await get('/platform/organizations/fly/{fly_organization_id}', { + params: { path: { fly_organization_id: flyOrganizationId } }, + }) + if (error) throw error + return data as { slug: string } +} + +type OrganizationByFlyOrgIdData = Awaited> + +export const useOrganizationByFlyOrgIdMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + return useMutation( + (vars) => getOrganizationByFlyOrgId(vars), + { + async onSuccess(data, variables, context) { + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to get organization: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + } + ) +} diff --git a/studio/data/organizations/organization-create-mutation.ts b/studio/data/organizations/organization-create-mutation.ts index 17faec33c0e..3e58e5ce921 100644 --- a/studio/data/organizations/organization-create-mutation.ts +++ b/studio/data/organizations/organization-create-mutation.ts @@ -15,22 +15,11 @@ export type OrganizationCreateVariables = { V2?: boolean } -export async function createOrganization({ - name, - kind, - size, - tier, - payment_method, - V2 = false, -}: OrganizationCreateVariables) { +export async function createOrganization({ name, V2 = false }: OrganizationCreateVariables) { const { data, error } = await post('/platform/organizations', { // @ts-ignore [Joshen] Generated API spec is wrong? body: { name, - kind, - ...(kind == 'COMPANY' ? { size } : {}), - ...(tier !== undefined ? { tier } : {}), - ...(payment_method !== undefined ? { payment_method } : {}), }, ...(V2 ? { headers: { Version: '2' } } : {}), }) diff --git a/studio/data/projects/project-by-fly-extension-id-mutation.ts b/studio/data/projects/project-by-fly-extension-id-mutation.ts new file mode 100644 index 00000000000..74456fa27d5 --- /dev/null +++ b/studio/data/projects/project-by-fly-extension-id-mutation.ts @@ -0,0 +1,47 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query' + +import { get } from 'data/fetchers' +import toast from 'react-hot-toast' +import { ResponseError } from 'types' + +export type ProjectByFlyExtensionIdVariables = { + flyExtensionId: string +} + +export async function getProjectByFlyExtensionId({ + flyExtensionId, +}: ProjectByFlyExtensionIdVariables) { + const { data, error } = await get('/platform/projects/fly/{fly_extension_id}', { + params: { path: { fly_extension_id: flyExtensionId } }, + }) + if (error) throw error + return data as { ref: string } +} + +type ProjectByFlyExtensionIdData = Awaited> + +export const useProjectByFlyExtensionIdMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + return useMutation( + (vars) => getProjectByFlyExtensionId(vars), + { + async onSuccess(data, variables, context) { + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to get project: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + } + ) +} diff --git a/studio/pages/sign-in-fly-tos.tsx b/studio/pages/sign-in-fly-tos.tsx index 0adf0d886fe..f76071e8e00 100644 --- a/studio/pages/sign-in-fly-tos.tsx +++ b/studio/pages/sign-in-fly-tos.tsx @@ -1,22 +1,60 @@ -import { useRouter } from 'next/router' import Link from 'next/link' import Image from 'next/image' +import { useRouter } from 'next/router' import { Button } from 'ui' import { useTheme } from 'next-themes' import { API_URL } from 'lib/constants' +import { useEffect, useState } from 'react' +import { useIsLoggedIn } from 'common' +import { useProjectByFlyExtensionIdMutation } from 'data/projects/project-by-fly-extension-id-mutation' +import { useOrganizationByFlyOrgIdMutation } from 'data/organizations/organization-by-fly-organization-id-mutation' const SignInFlyTos = () => { + const [loading, setLoading] = useState(true) + const isLoggedIn = useIsLoggedIn() const router = useRouter() const { isReady, - query: { extension_id, organization_id }, + query: { fly_extension_id, fly_organization_id }, } = router const { resolvedTheme } = useTheme() + const { mutateAsync: getProjectByFlyExtensionId } = useProjectByFlyExtensionIdMutation({ + onSuccess: (res) => { + router.replace(`/project/${res.ref}`) + }, + onError: (_) => { + setLoading(false) + }, + }) + const { mutateAsync: getOrgByFlyOrgId } = useOrganizationByFlyOrgIdMutation({ + onSuccess: (_) => { + router.replace('/projects') + }, + onError: (_) => { + setLoading(false) + }, + }) + + useEffect(() => { + if (!isReady) { + return + } + if (!isLoggedIn) { + setLoading(false) + return + } + + fly_extension_id + ? getProjectByFlyExtensionId({ flyExtensionId: fly_extension_id as string }) + : fly_organization_id + ? getOrgByFlyOrgId({ flyOrganizationId: fly_organization_id as string }) + : setLoading(false) + }, [isReady]) const onSignInWithFly = async () => { - window.location.href = extension_id - ? `${API_URL}/tos/fly?extension_id=${extension_id}` - : `${API_URL}/tos/fly?organization_id=${organization_id}` + window.location.href = fly_extension_id + ? `${API_URL}/tos/fly?extension_id=${fly_extension_id}` + : `${API_URL}/tos/fly?organization_id=${fly_organization_id}` } return ( @@ -46,16 +84,20 @@ const SignInFlyTos = () => {

Continue to Supabase Dashboard

-
- - {isReady && !extension_id && !organization_id && ( -

- An extension ID or organization ID is required to login -

- )} -
+ {loading ? ( +

Checking your access rights...

+ ) : ( +
+ + {isReady && !fly_extension_id && !fly_organization_id && ( +

+ A fly extension ID or organization ID is required to login +

+ )} +
+ )}

By continuing, you agree to Supabase's{' '}