diff --git a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
index 84aa2e5e7d0..d6958104d99 100644
--- a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
+++ b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
@@ -1,4 +1,6 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
+
+import { useParams } from 'common'
import { observer } from 'mobx-react-lite'
import { useEffect, useState } from 'react'
import {
@@ -17,7 +19,6 @@ import {
} from 'ui'
import { boolean, number, object, string } from 'yup'
-import { useParams } from 'common'
import {
FormActions,
FormHeader,
@@ -26,9 +27,11 @@ import {
FormSectionContent,
FormSectionLabel,
} from 'components/ui/Forms'
+import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
-import { useCheckPermissions, useStore } from 'hooks'
+import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
+import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
const schema = object({
DISABLE_SIGNUP: boolean().required(),
@@ -44,8 +47,20 @@ const schema = object({
.oneOf(['hcaptcha', 'turnstile'])
.required('Captcha provider must be either hcaptcha or turnstile'),
}),
+ SESSIONS_TIMEBOX: number().min(0, 'Must be a positive number'),
+ SESSIONS_INACTIVITY_TIMEOUT: number().min(0, 'Must be a positive number'),
})
+function HoursOrNeverText({ value }: { value: number }) {
+ if (value === 0) {
+ return 'never'
+ } else if (value === 1) {
+ return 'hour'
+ } else {
+ return 'hours'
+ }
+}
+
const BasicAuthSettingsForm = observer(() => {
const { ui } = useStore()
const { ref: projectRef } = useParams()
@@ -62,12 +77,21 @@ const BasicAuthSettingsForm = observer(() => {
const [hidden, setHidden] = useState(true)
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
+ const organization = useSelectedOrganization()
+ const { data: subscription, isSuccess: isSuccessSubscription } = useOrgSubscriptionQuery({
+ orgSlug: organization!.slug,
+ })
+
+ const isProPlanAndUp = isSuccessSubscription && subscription?.plan?.id !== 'free'
+
const INITIAL_VALUES = {
DISABLE_SIGNUP: !authConfig?.DISABLE_SIGNUP,
SITE_URL: authConfig?.SITE_URL,
SECURITY_CAPTCHA_ENABLED: authConfig?.SECURITY_CAPTCHA_ENABLED || false,
SECURITY_CAPTCHA_SECRET: authConfig?.SECURITY_CAPTCHA_SECRET || '',
SECURITY_CAPTCHA_PROVIDER: authConfig?.SECURITY_CAPTCHA_PROVIDER || 'hcaptcha',
+ SESSIONS_TIMEBOX: authConfig?.SESSIONS_TIMEBOX || 0,
+ SESSIONS_INACTIVITY_TIMEOUT: authConfig?.SESSIONS_INACTIVITY_TIMEOUT || 0,
}
const onSubmit = (values: any, { resetForm }: any) => {
@@ -152,6 +176,45 @@ const BasicAuthSettingsForm = observer(() => {
+ User Sessions}>
+
+ {isProPlanAndUp ? (
+ <>>
+ ) : (
+
+ )}
+
+
+
+ }
+ disabled={!canUpdateConfig || !isProPlanAndUp}
+ />
+
+
+
+ }
+ disabled={!canUpdateConfig || !isProPlanAndUp}
+ />
+
+
+
Bot and Abuse Protection}>
{
await createOrganization({
name: orgName,
kind: orgKind,
- tier: 'tier_' + dbTier.toLowerCase(),
+ tier: ('tier_' + dbTier.toLowerCase()) as
+ | 'tier_payg'
+ | 'tier_pro'
+ | 'tier_free'
+ | 'tier_team'
+ | 'tier_enterprise',
...(orgKind == 'COMPANY' ? { size: orgSize } : {}),
payment_method: paymentMethodId,
})
diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx
index cc87d1e3087..a244632ff1b 100644
--- a/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx
+++ b/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx
@@ -102,7 +102,7 @@ const MemberActions = ({ member, roles }: MemberActionsProps) => {
await asyncDeleteMemberInvite({ slug, invitedId, invalidateDetail: false })
createOrganizationMemberInvite({
slug,
- invitedEmail: member.primary_email,
+ invitedEmail: member.primary_email!,
ownerId: invitedId,
roleId: roleId,
})
diff --git a/apps/studio/data/api.d.ts b/apps/studio/data/api.d.ts
index ca72c8adcfc..0a4a682708e 100644
--- a/apps/studio/data/api.d.ts
+++ b/apps/studio/data/api.d.ts
@@ -13,10 +13,6 @@ type OneOf = T extends [infer Only]
: never
export interface paths {
- '/platform/login': {
- /** Redirects to dashboard homepage */
- get: operations['LoginController_redirectToDashboardHomepage']
- }
'/platform/notifications': {
/** Get notifications */
get: operations['NotificationsController_getNotificationsV2']
@@ -547,7 +543,15 @@ export interface paths {
}
'/platform/projects/{ref}/analytics/endpoints/functions.inv-stats': {
/** Gets a project's function invocation statistics */
- get: operations['FunctionLogsController_getStatus']
+ get: operations['FunctionInvocationLogsController_getStatus']
+ }
+ '/platform/projects/{ref}/analytics/endpoints/functions.req-stats': {
+ /** Gets a project's function request statistics */
+ get: operations['FunctionRequestLogsController_getStatus']
+ }
+ '/platform/projects/{ref}/analytics/endpoints/functions.resource-usage': {
+ /** Gets a project's function resource usage */
+ get: operations['FunctionResourceLogsController_getStatus']
}
'/platform/projects/{ref}/analytics/endpoints/logs.all': {
/** Gets project's logs */
@@ -1269,7 +1273,15 @@ export interface paths {
}
'/v0/projects/{ref}/analytics/endpoints/functions.inv-stats': {
/** Gets a project's function invocation statistics */
- get: operations['FunctionLogsController_getStatus']
+ get: operations['FunctionInvocationLogsController_getStatus']
+ }
+ '/v0/projects/{ref}/analytics/endpoints/functions.req-stats': {
+ /** Gets a project's function request statistics */
+ get: operations['FunctionRequestLogsController_getStatus']
+ }
+ '/v0/projects/{ref}/analytics/endpoints/functions.resource-usage': {
+ /** Gets a project's function resource usage */
+ get: operations['FunctionResourceLogsController_getStatus']
}
'/v0/projects/{ref}/analytics/endpoints/logs.all': {
/** Gets project's logs */
@@ -1691,15 +1703,28 @@ export interface components {
ids: string[]
}
NotificationResponseV2: {
+ /** @enum {string} */
+ type:
+ | 'project.tier-limit-exceeded'
+ | 'postgresql.upgrade-available'
+ | 'postgresql.upgrade-completed'
+ | 'project.update-completed'
+ | 'project.informational'
+ /** @enum {string} */
+ status: 'new' | 'seen' | 'archived'
+ /** @enum {string} */
+ priority: 'Critical' | 'Warning' | 'Info'
id: string
inserted_at: string
- type: Record
- status: Record
- priority: Record
name: string
data: Record
meta: Record
}
+ UpdateNotificationBodyV2: {
+ id: string
+ /** @enum {string} */
+ status: 'new' | 'seen' | 'archived'
+ }
ResetPasswordBody: {
email: string
redirectTo?: string
@@ -1738,6 +1763,8 @@ export interface components {
redirectTo?: string
}
ProjectResourceWarningsResponse: {
+ /** @enum {string|null} */
+ need_pitr: 'critical' | 'warning' | null
/** @enum {string|null} */
disk_io_exhaustion: 'critical' | 'warning' | null
/** @enum {string|null} */
@@ -1764,6 +1791,7 @@ export interface components {
SMTP_MAX_FREQUENCY: number
SMTP_SENDER_NAME?: string
MAILER_AUTOCONFIRM: boolean
+ MAILER_ALLOW_UNVERIFIED_EMAIL_SIGN_INS: boolean
MAILER_SUBJECTS_INVITE: string
MAILER_SUBJECTS_CONFIRMATION: string
MAILER_SUBJECTS_RECOVERY: string
@@ -1782,6 +1810,8 @@ export interface components {
SECURITY_CAPTCHA_ENABLED: boolean
SECURITY_CAPTCHA_PROVIDER: string
SECURITY_CAPTCHA_SECRET: string
+ SESSIONS_TIMEBOX?: number
+ SESSIONS_INACTIVITY_TIMEOUT?: number
RATE_LIMIT_EMAIL_SENT: number
RATE_LIMIT_SMS_SENT: number
RATE_LIMIT_VERIFY?: number
@@ -1893,6 +1923,7 @@ export interface components {
SMTP_PASS_ENCRYPTED?: string | null
SMTP_MAX_FREQUENCY?: number
SMTP_SENDER_NAME?: string
+ MAILER_ALLOW_UNVERIFIED_EMAIL_SIGN_INS?: boolean
MAILER_AUTOCONFIRM?: boolean
MAILER_SUBJECTS_INVITE?: string
MAILER_SUBJECTS_CONFIRMATION?: string
@@ -1912,6 +1943,8 @@ export interface components {
SECURITY_CAPTCHA_ENABLED?: boolean
SECURITY_CAPTCHA_PROVIDER?: string
SECURITY_CAPTCHA_SECRET?: string
+ SESSIONS_TIMEBOX?: number | null
+ SESSIONS_INACTIVITY_TIMEOUT?: number | null
RATE_LIMIT_EMAIL_SENT?: number
RATE_LIMIT_SMS_SENT?: number
RATE_LIMIT_VERIFY?: number
@@ -2025,6 +2058,7 @@ export interface components {
SMTP_MAX_FREQUENCY: number
SMTP_SENDER_NAME?: string
MAILER_AUTOCONFIRM: boolean
+ MAILER_ALLOW_UNVERIFIED_EMAIL_SIGN_INS: boolean
MAILER_SUBJECTS_INVITE: string
MAILER_SUBJECTS_CONFIRMATION: string
MAILER_SUBJECTS_RECOVERY: string
@@ -2043,6 +2077,8 @@ export interface components {
SECURITY_CAPTCHA_ENABLED: boolean
SECURITY_CAPTCHA_PROVIDER: string
SECURITY_CAPTCHA_SECRET: string
+ SESSIONS_TIMEBOX?: number
+ SESSIONS_INACTIVITY_TIMEOUT?: number
RATE_LIMIT_EMAIL_SENT: number
RATE_LIMIT_SMS_SENT: number
RATE_LIMIT_VERIFY?: number
@@ -2190,7 +2226,6 @@ export interface components {
inserted_at: string
}
BackupsResponse: {
- tierId: string
tierKey: string
region: string
walg_enabled: boolean
@@ -2234,14 +2269,25 @@ export interface components {
migrated_at: string | null
}
OrganizationResponse: {
- id: string
+ id: number
+ slug: string
name: string
+ billing_email: string
+ is_owner: boolean
+ stripe_customer_id: string
+ subscription_id?: string
+ opt_in_tags: string[]
}
GetOrganizationByFlyOrganizationIdResponse: {
slug: string
}
CreateOrganizationBody: {
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
@@ -2463,9 +2509,10 @@ export interface components {
}
Member: {
gotrue_id: string
- primary_email: string
+ primary_email: string | null
role_ids: number[]
username: string
+ mfa_enabled: boolean
}
UpdateMemberBody: {
role_id: number
@@ -3307,7 +3354,6 @@ export interface components {
region: string
status: string
subscription_id: string
- is_readonly_mode_enabled?: boolean
is_branch_enabled: boolean
preview_branch_refs: string[]
disk_volume_size_gb?: number
@@ -3368,7 +3414,6 @@ export interface components {
region: string
status: string
subscription_id: string
- is_readonly_mode_enabled?: boolean
is_branch_enabled: boolean
preview_branch_refs: string[]
disk_volume_size_gb?: number
@@ -4147,6 +4192,11 @@ export interface components {
branch: string
label?: string
}
+ CreateCliLoginSessionBody: {
+ session_id: string
+ public_key: string
+ token_name?: string
+ }
FunctionResponse: {
id: string
slug: string
@@ -4180,14 +4230,13 @@ export interface components {
| 'ACTIVE_UNHEALTHY'
| 'COMING_UP'
| 'GOING_DOWN'
- | 'INACTIVE'
| 'INIT_FAILED'
| 'REMOVED'
| 'RESTORING'
| 'UNKNOWN'
| 'UPGRADING'
- | 'PAUSING'
reportingToken: string
+ databaseIdentifier: string
}
EventBody: {
reportingToken: string
@@ -4553,11 +4602,19 @@ export interface components {
entrypoint_path?: string
import_map_path?: string
}
+ OrganizationResponseV1: {
+ id: string
+ name: string
+ }
+ CreateOrganizationBodyV1: {
+ name: string
+ }
V1OrganizationMemberResponse: {
user_id: string
user_name: string
email?: string
role_name: string
+ mfa_enabled: boolean
}
OAuthTokenBody: {
/** @enum {string} */
@@ -4745,14 +4802,6 @@ export type $defs = Record
export type external = Record
export interface operations {
- /** Redirects to dashboard homepage */
- LoginController_redirectToDashboardHomepage: {
- responses: {
- 200: {
- content: never
- }
- }
- }
/** Get notifications */
NotificationsController_getNotificationsV2: {
parameters: {
@@ -4797,7 +4846,7 @@ export interface operations {
NotificationsController_updateNotificationsV2: {
requestBody: {
content: {
- 'application/json': string[]
+ 'application/json': components['schemas']['UpdateNotificationBodyV2'][]
}
}
responses: {
@@ -5451,7 +5500,7 @@ export interface operations {
responses: {
200: {
content: {
- 'application/json': components['schemas']['OrganizationResponse'][]
+ 'application/json': components['schemas']['OrganizationResponseV1'][]
}
}
/** @description Unexpected error listing organizations */
@@ -8666,7 +8715,7 @@ export interface operations {
}
}
/** Gets a project's function invocation statistics */
- FunctionLogsController_getStatus: {
+ FunctionInvocationLogsController_getStatus: {
parameters: {
query: {
interval: '5min' | '15min' | '1hr' | '1day' | '7day'
@@ -8692,6 +8741,60 @@ export interface operations {
}
}
}
+ /** Gets a project's function request statistics */
+ FunctionRequestLogsController_getStatus: {
+ parameters: {
+ query: {
+ interval: '5min' | '15min' | '1hr' | '1day' | '7day'
+ function_id: string
+ }
+ path: {
+ /** @description Project ref */
+ ref: string
+ }
+ }
+ responses: {
+ 200: {
+ content: {
+ 'application/json': components['schemas']['AnalyticsResponse']
+ }
+ }
+ 403: {
+ content: never
+ }
+ /** @description Failed to get project's function request statistics */
+ 500: {
+ content: never
+ }
+ }
+ }
+ /** Gets a project's function resource usage */
+ FunctionResourceLogsController_getStatus: {
+ parameters: {
+ query: {
+ interval: '5min' | '15min' | '1hr' | '1day' | '7day'
+ function_id: string
+ }
+ path: {
+ /** @description Project ref */
+ ref: string
+ }
+ }
+ responses: {
+ 200: {
+ content: {
+ 'application/json': components['schemas']['AnalyticsResponse']
+ }
+ }
+ 403: {
+ content: never
+ }
+ /** @description Failed to get project's function resource usage */
+ 500: {
+ content: never
+ }
+ }
+ }
/** Gets project's logs */
LogsController_getApiPaths: {
parameters: {
@@ -10124,6 +10227,40 @@ export interface operations {
}
}
}
+ /** Create CLI login session */
+ CliLoginController_createCliLoginSession: {
+ requestBody: {
+ content: {
+ 'application/json': components['schemas']['CreateCliLoginSessionBody']
+ }
+ }
+ responses: {
+ 201: {
+ content: never
+ }
+ /** @description Failed to create CLI login session */
+ 500: {
+ content: never
+ }
+ }
+ }
+ /** Retrieve CLI login session */
+ CliLoginController_getCliLoginSession: {
+ parameters: {
+ path: {
+ session_id: string
+ }
+ }
+ responses: {
+ 200: {
+ content: never
+ }
+ /** @description Failed to retrieve CLI login session */
+ 500: {
+ content: never
+ }
+ }
+ }
/** Gets GoTrue template */
AuthTemplateController_getTemplate: {
parameters: {
@@ -10624,6 +10761,7 @@ export interface operations {
parameters: {
header: {
'x-github-delivery': string
+ 'x-github-event': string
'x-hub-signature-256': string
}
}
@@ -11954,13 +12092,13 @@ export interface operations {
OrganizationsController_createOrganization: {
requestBody: {
content: {
- 'application/json': components['schemas']['CreateOrganizationBody']
+ 'application/json': components['schemas']['CreateOrganizationBodyV1']
}
}
responses: {
201: {
content: {
- 'application/json': components['schemas']['OrganizationResponse']
+ 'application/json': components['schemas']['OrganizationResponseV1']
}
}
/** @description Unexpected error creating an organization */
diff --git a/apps/studio/data/organizations/organization-create-mutation.ts b/apps/studio/data/organizations/organization-create-mutation.ts
index bbb236e7596..40966f240ee 100644
--- a/apps/studio/data/organizations/organization-create-mutation.ts
+++ b/apps/studio/data/organizations/organization-create-mutation.ts
@@ -10,7 +10,7 @@ export type OrganizationCreateVariables = {
name: string
kind?: string
size?: string
- tier?: string
+ tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise'
payment_method?: string
}
diff --git a/apps/studio/data/organizations/organization-members-query.ts b/apps/studio/data/organizations/organization-members-query.ts
index fbed13d829f..bb7d124e428 100644
--- a/apps/studio/data/organizations/organization-members-query.ts
+++ b/apps/studio/data/organizations/organization-members-query.ts
@@ -1,9 +1,9 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
+import { components } from 'data/api'
import { get } from 'data/fetchers'
import { ResponseError } from 'types'
import { organizationKeys } from './keys'
-import { components } from 'data/api'
export type OrganizationMembersVariables = {
slug?: string
@@ -13,10 +13,6 @@ type Member = components['schemas']['Member']
export interface OrganizationMember extends Member {
invited_at?: string
invited_id?: number
- // [Joshen] Proactively adding this to support showing if member has MFA enabled
- // can be removed once infra API changes from the following PR is in
- // https://github.com/supabase/infrastructure/pull/15725
- mfa_enabled?: boolean
}
export async function getOrganizationMembers(