mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
Fix error where deleting a team creator default permission would make the dashboard crash
This commit is contained in:
parent
09d1156142
commit
037068e432
@ -1,6 +1,6 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { Config, getInvalidConfigReason, normalize, override } from "@stackframe/stack-shared/dist/config/format";
|
||||
import { BranchConfigOverride, BranchConfigOverrideOverride, BranchIncompleteConfig, BranchRenderedConfig, EnvironmentConfigOverride, EnvironmentConfigOverrideOverride, EnvironmentIncompleteConfig, EnvironmentRenderedConfig, OrganizationConfigOverride, OrganizationConfigOverrideOverride, OrganizationIncompleteConfig, OrganizationRenderedConfig, ProjectConfigOverride, ProjectConfigOverrideOverride, ProjectIncompleteConfig, ProjectRenderedConfig, applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, getConfigOverrideErrors, getIncompleteConfigWarnings, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { BranchConfigOverride, BranchConfigOverrideOverride, BranchIncompleteConfig, BranchRenderedConfig, CompleteConfig, EnvironmentConfigOverride, EnvironmentConfigOverrideOverride, EnvironmentIncompleteConfig, EnvironmentRenderedConfig, OrganizationConfigOverride, OrganizationConfigOverrideOverride, OrganizationIncompleteConfig, ProjectConfigOverride, ProjectConfigOverrideOverride, ProjectIncompleteConfig, ProjectRenderedConfig, applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, getConfigOverrideErrors, getIncompleteConfigWarnings, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
|
||||
import { yupBoolean, yupMixed, yupObject, yupRecord, yupString, yupUnion } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { isTruthy } from "@stackframe/stack-shared/dist/utils/booleans";
|
||||
@ -9,7 +9,8 @@ import { filterUndefined, typedEntries } from "@stackframe/stack-shared/dist/uti
|
||||
import { Result } from "@stackframe/stack-shared/dist/utils/results";
|
||||
import { deindent, stringCompare } from "@stackframe/stack-shared/dist/utils/strings";
|
||||
import * as yup from "yup";
|
||||
import { PrismaClientTransaction, RawQuery, globalPrismaClient, rawQuery } from "../prisma-client";
|
||||
import { RawQuery, globalPrismaClient, rawQuery } from "../prisma-client";
|
||||
import { listPermissionDefinitionsFromConfig } from "./permissions";
|
||||
import { DEFAULT_BRANCH_ID } from "./tenancies";
|
||||
|
||||
type ProjectOptions = { projectId: string };
|
||||
@ -46,7 +47,7 @@ export function getRenderedEnvironmentConfigQuery(options: EnvironmentOptions):
|
||||
);
|
||||
}
|
||||
|
||||
export function getRenderedOrganizationConfigQuery(options: OrganizationOptions): RawQuery<Promise<OrganizationRenderedConfig>> {
|
||||
export function getRenderedOrganizationConfigQuery(options: OrganizationOptions): RawQuery<Promise<CompleteConfig>> {
|
||||
return RawQuery.then(
|
||||
getIncompleteOrganizationConfigQuery(options),
|
||||
async (incompleteConfig) => await sanitizeOrganizationConfig(normalize(applyOrganizationDefaults(await incompleteConfig), { onDotIntoNonObject: "ignore" }) as any),
|
||||
@ -469,7 +470,7 @@ import.meta.vitest?.test('_validateConfigOverrideSchemaImpl(...)', async ({ expe
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// C -> A
|
||||
export const renderedOrganizationConfigToProjectCrud = (renderedConfig: OrganizationRenderedConfig): ProjectsCrud["Admin"]["Read"]['config'] => {
|
||||
export const renderedOrganizationConfigToProjectCrud = (renderedConfig: CompleteConfig): ProjectsCrud["Admin"]["Read"]['config'] => {
|
||||
const oauthProviders = typedEntries(renderedConfig.auth.oauth.providers)
|
||||
.map(([oauthProviderId, oauthProvider]) => {
|
||||
if (!oauthProvider.type) {
|
||||
@ -491,6 +492,15 @@ export const renderedOrganizationConfigToProjectCrud = (renderedConfig: Organiza
|
||||
.filter(isTruthy)
|
||||
.sort((a, b) => stringCompare(a.id, b.id));
|
||||
|
||||
const teamPermissionDefinitions = listPermissionDefinitionsFromConfig({
|
||||
config: renderedConfig,
|
||||
scope: "team",
|
||||
});
|
||||
const projectPermissionDefinitions = listPermissionDefinitionsFromConfig({
|
||||
config: renderedConfig,
|
||||
scope: "project",
|
||||
});
|
||||
|
||||
return {
|
||||
allow_localhost: renderedConfig.domains.allowLocalhost,
|
||||
client_team_creation_enabled: renderedConfig.teams.allowClientTeamCreation,
|
||||
@ -527,15 +537,15 @@ export const renderedOrganizationConfigToProjectCrud = (renderedConfig: Organiza
|
||||
email_theme: renderedConfig.emails.selectedThemeId,
|
||||
|
||||
team_creator_default_permissions: typedEntries(renderedConfig.rbac.defaultPermissions.teamCreator)
|
||||
.filter(([_, perm]) => perm)
|
||||
.filter(([id, perm]) => perm && teamPermissionDefinitions.some((p) => p.id === id))
|
||||
.map(([id, perm]) => ({ id }))
|
||||
.sort((a, b) => stringCompare(a.id, b.id)),
|
||||
team_member_default_permissions: typedEntries(renderedConfig.rbac.defaultPermissions.teamMember)
|
||||
.filter(([_, perm]) => perm)
|
||||
.filter(([id, perm]) => perm && teamPermissionDefinitions.some((p) => p.id === id))
|
||||
.map(([id, perm]) => ({ id }))
|
||||
.sort((a, b) => stringCompare(a.id, b.id)),
|
||||
user_default_permissions: typedEntries(renderedConfig.rbac.defaultPermissions.signUp)
|
||||
.filter(([_, perm]) => perm)
|
||||
.filter(([id, perm]) => perm && projectPermissionDefinitions.some((p) => p.id === id))
|
||||
.map(([id, perm]) => ({ id }))
|
||||
.sort((a, b) => stringCompare(a.id, b.id)),
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { OrganizationRenderedConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { CompleteConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { ProjectPermissionsCrud } from "@stackframe/stack-shared/dist/interface/crud/project-permissions";
|
||||
import { TeamPermissionDefinitionsCrud, TeamPermissionsCrud } from "@stackframe/stack-shared/dist/interface/crud/team-permissions";
|
||||
import { groupBy } from "@stackframe/stack-shared/dist/utils/arrays";
|
||||
@ -158,15 +158,13 @@ export async function revokeTeamPermission(
|
||||
});
|
||||
}
|
||||
|
||||
export async function listPermissionDefinitions(
|
||||
export function listPermissionDefinitionsFromConfig(
|
||||
options: {
|
||||
config: CompleteConfig,
|
||||
scope: "team" | "project",
|
||||
tenancy: Tenancy,
|
||||
}
|
||||
): Promise<(TeamPermissionDefinitionsCrud["Admin"]["Read"])[]> {
|
||||
const renderedConfig = options.tenancy.config;
|
||||
|
||||
const permissions = typedEntries(renderedConfig.rbac.permissions).filter(([_, p]) => p.scope === options.scope);
|
||||
},
|
||||
) {
|
||||
const permissions = typedEntries(options.config.rbac.permissions).filter(([_, p]) => p.scope === options.scope);
|
||||
|
||||
return [
|
||||
...permissions.map(([id, p]) => ({
|
||||
@ -182,6 +180,18 @@ export async function listPermissionDefinitions(
|
||||
].sort((a, b) => stringCompare(a.id, b.id));
|
||||
}
|
||||
|
||||
export async function listPermissionDefinitions(
|
||||
options: {
|
||||
scope: "team" | "project",
|
||||
tenancy: Tenancy,
|
||||
}
|
||||
): Promise<(TeamPermissionDefinitionsCrud["Admin"]["Read"])[]> {
|
||||
return listPermissionDefinitionsFromConfig({
|
||||
config: options.tenancy.config,
|
||||
scope: options.scope,
|
||||
});
|
||||
}
|
||||
|
||||
export async function createPermissionDefinition(
|
||||
globalTx: PrismaTransaction,
|
||||
options: {
|
||||
@ -196,7 +206,7 @@ export async function createPermissionDefinition(
|
||||
) {
|
||||
const oldConfig = options.tenancy.config;
|
||||
|
||||
const existingPermission = oldConfig.rbac.permissions[options.data.id] as OrganizationRenderedConfig['rbac']['permissions'][string] | undefined;
|
||||
const existingPermission = oldConfig.rbac.permissions[options.data.id] as CompleteConfig['rbac']['permissions'][string] | undefined;
|
||||
const allIds = Object.keys(oldConfig.rbac.permissions)
|
||||
.filter(id => oldConfig.rbac.permissions[id].scope === options.scope)
|
||||
.concat(Object.keys(options.scope === "team" ? teamSystemPermissionMap : {}));
|
||||
@ -249,7 +259,7 @@ export async function updatePermissionDefinition(
|
||||
const newId = options.data.id ?? options.oldId;
|
||||
const oldConfig = options.tenancy.config;
|
||||
|
||||
const existingPermission = oldConfig.rbac.permissions[options.oldId] as OrganizationRenderedConfig['rbac']['permissions'][string] | undefined;
|
||||
const existingPermission = oldConfig.rbac.permissions[options.oldId] as CompleteConfig['rbac']['permissions'][string] | undefined;
|
||||
|
||||
if (!existingPermission) {
|
||||
throw new KnownErrors.PermissionNotFound(options.oldId);
|
||||
@ -335,7 +345,7 @@ export async function deletePermissionDefinition(
|
||||
) {
|
||||
const oldConfig = options.tenancy.config;
|
||||
|
||||
const existingPermission = oldConfig.rbac.permissions[options.permissionId] as OrganizationRenderedConfig['rbac']['permissions'][string] | undefined;
|
||||
const existingPermission = oldConfig.rbac.permissions[options.permissionId] as CompleteConfig['rbac']['permissions'][string] | undefined;
|
||||
|
||||
if (!existingPermission || existingPermission.scope !== options.scope) {
|
||||
throw new KnownErrors.PermissionNotFound(options.permissionId);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { EnvironmentConfigOverrideOverride, OrganizationRenderedConfig, ProjectConfigOverrideOverride } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { CompleteConfig, EnvironmentConfigOverrideOverride, ProjectConfigOverrideOverride } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { AdminUserProjectsCrud, ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
|
||||
import { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/users";
|
||||
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
|
||||
@ -158,7 +158,7 @@ export async function createOrUpdateProjectWithLegacyConfig(
|
||||
microsoftTenantId: provider.microsoft_tenant_id,
|
||||
allowSignIn: true,
|
||||
allowConnectedAccounts: true,
|
||||
} satisfies OrganizationRenderedConfig['auth']['oauth']['providers'][string]
|
||||
} satisfies CompleteConfig['auth']['oauth']['providers'][string]
|
||||
];
|
||||
})) : undefined,
|
||||
// ======================= users =======================
|
||||
@ -174,7 +174,7 @@ export async function createOrUpdateProjectWithLegacyConfig(
|
||||
{
|
||||
baseUrl: domain.domain,
|
||||
handlerPath: domain.handler_path,
|
||||
} satisfies OrganizationRenderedConfig['domains']['trustedDomains'][string],
|
||||
} satisfies CompleteConfig['domains']['trustedDomains'][string],
|
||||
];
|
||||
})) : undefined,
|
||||
// ======================= api keys =======================
|
||||
@ -189,7 +189,7 @@ export async function createOrUpdateProjectWithLegacyConfig(
|
||||
password: dataOptions.email_config.password,
|
||||
senderName: dataOptions.email_config.sender_name,
|
||||
senderEmail: dataOptions.email_config.sender_email,
|
||||
} satisfies OrganizationRenderedConfig['emails']['server'] : undefined,
|
||||
} satisfies CompleteConfig['emails']['server'] : undefined,
|
||||
'emails.selectedThemeId': dataOptions.email_theme,
|
||||
// ======================= rbac =======================
|
||||
'rbac.defaultPermissions.teamMember': translateDefaultPermissions(dataOptions.team_member_default_permissions),
|
||||
@ -205,7 +205,7 @@ export async function createOrUpdateProjectWithLegacyConfig(
|
||||
'$read_members': true,
|
||||
'$invite_members': true,
|
||||
},
|
||||
} satisfies OrganizationRenderedConfig['rbac']['permissions'][string];
|
||||
} satisfies CompleteConfig['rbac']['permissions'][string];
|
||||
configOverrideOverride['rbac.permissions.team_admin'] ??= {
|
||||
description: "Default permission for team admins",
|
||||
scope: "team",
|
||||
@ -217,7 +217,7 @@ export async function createOrUpdateProjectWithLegacyConfig(
|
||||
'$invite_members': true,
|
||||
'$manage_api_keys': true,
|
||||
},
|
||||
} satisfies OrganizationRenderedConfig['rbac']['permissions'][string];
|
||||
} satisfies CompleteConfig['rbac']['permissions'][string];
|
||||
|
||||
configOverrideOverride['rbac.defaultPermissions.teamCreator'] ??= { 'team_admin': true };
|
||||
configOverrideOverride['rbac.defaultPermissions.teamMember'] ??= { 'team_member': true };
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { PrismaNeon } from "@prisma/adapter-neon";
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import { Prisma, PrismaClient } from "@prisma/client";
|
||||
import { OrganizationRenderedConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { CompleteConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { getEnvVariable, getNodeEnvironment } from '@stackframe/stack-shared/dist/utils/env';
|
||||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { globalVar } from "@stackframe/stack-shared/dist/utils/globals";
|
||||
@ -70,7 +70,7 @@ function getPostgresPrismaClient(connectionString: string) {
|
||||
return postgresPrismaClient;
|
||||
}
|
||||
|
||||
export async function getPrismaClientForSourceOfTruth(sourceOfTruth: OrganizationRenderedConfig["sourceOfTruth"], branchId: string) {
|
||||
export async function getPrismaClientForSourceOfTruth(sourceOfTruth: CompleteConfig["sourceOfTruth"], branchId: string) {
|
||||
switch (sourceOfTruth.type) {
|
||||
case 'neon': {
|
||||
if (!(branchId in sourceOfTruth.connectionStrings)) {
|
||||
@ -92,7 +92,7 @@ export async function getPrismaClientForSourceOfTruth(sourceOfTruth: Organizatio
|
||||
}
|
||||
}
|
||||
|
||||
export function getPrismaSchemaForSourceOfTruth(sourceOfTruth: OrganizationRenderedConfig["sourceOfTruth"], branchId: string) {
|
||||
export function getPrismaSchemaForSourceOfTruth(sourceOfTruth: CompleteConfig["sourceOfTruth"], branchId: string) {
|
||||
switch (sourceOfTruth.type) {
|
||||
case 'postgres': {
|
||||
return getSchemaFromConnectionString(sourceOfTruth.connectionString);
|
||||
|
||||
@ -518,3 +518,101 @@ it("cannot update a team permission definition to contain a permission that does
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("removes deleted permission definition from team creator default permissions", async ({ expect }) => {
|
||||
backendContext.set({ projectKeys: InternalProjectKeys });
|
||||
const { adminAccessToken } = await Project.createAndGetAdminToken();
|
||||
|
||||
// Step 1: Create a new team permission definition
|
||||
const createPermissionResponse = await niceBackendFetch(`/api/v1/team-permission-definitions`, {
|
||||
accessType: "admin",
|
||||
method: "POST",
|
||||
body: {
|
||||
id: 'custom_team_permission',
|
||||
description: "Custom team permission for testing"
|
||||
},
|
||||
headers: {
|
||||
'x-stack-admin-access-token': adminAccessToken
|
||||
},
|
||||
});
|
||||
expect(createPermissionResponse).toMatchInlineSnapshot(`
|
||||
NiceResponse {
|
||||
"status": 201,
|
||||
"body": {
|
||||
"contained_permission_ids": [],
|
||||
"description": "Custom team permission for testing",
|
||||
"id": "custom_team_permission",
|
||||
},
|
||||
"headers": Headers { <some fields may have been hidden> },
|
||||
}
|
||||
`);
|
||||
|
||||
// Step 2: Update project to set team creator default permissions to include the new permission
|
||||
const updateProjectResponse = await niceBackendFetch(`/api/v1/internal/projects/current`, {
|
||||
accessType: "admin",
|
||||
method: "PATCH",
|
||||
body: {
|
||||
config: {
|
||||
team_creator_default_permissions: [
|
||||
{ id: "team_admin" },
|
||||
{ id: "custom_team_permission" }
|
||||
]
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'x-stack-admin-access-token': adminAccessToken
|
||||
},
|
||||
});
|
||||
expect(updateProjectResponse.status).toBe(200);
|
||||
expect(updateProjectResponse.body.config.team_creator_default_permissions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{ "id": "custom_team_permission" },
|
||||
{ "id": "team_admin" },
|
||||
]
|
||||
`);
|
||||
|
||||
// Step 3: Verify the permission is in the team creator default permissions
|
||||
const getProjectResponse1 = await niceBackendFetch(`/api/v1/internal/projects/current`, {
|
||||
accessType: "admin",
|
||||
method: "GET",
|
||||
headers: {
|
||||
'x-stack-admin-access-token': adminAccessToken
|
||||
},
|
||||
});
|
||||
expect(getProjectResponse1.status).toBe(200);
|
||||
expect(getProjectResponse1.body.config.team_creator_default_permissions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{ "id": "custom_team_permission" },
|
||||
{ "id": "team_admin" },
|
||||
]
|
||||
`);
|
||||
|
||||
// Step 4: Delete the permission definition
|
||||
const deletePermissionResponse = await niceBackendFetch(`/api/v1/team-permission-definitions/custom_team_permission`, {
|
||||
accessType: "admin",
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
'x-stack-admin-access-token': adminAccessToken
|
||||
},
|
||||
});
|
||||
expect(deletePermissionResponse).toMatchInlineSnapshot(`
|
||||
NiceResponse {
|
||||
"status": 200,
|
||||
"body": { "success": true },
|
||||
"headers": Headers { <some fields may have been hidden> },
|
||||
}
|
||||
`);
|
||||
|
||||
// Step 5: Verify the deleted permission is no longer in team creator default permissions
|
||||
const getProjectResponse2 = await niceBackendFetch(`/api/v1/internal/projects/current`, {
|
||||
accessType: "admin",
|
||||
method: "GET",
|
||||
headers: {
|
||||
'x-stack-admin-access-token': adminAccessToken
|
||||
},
|
||||
});
|
||||
expect(getProjectResponse2.status).toBe(200);
|
||||
expect(getProjectResponse2.body.config.team_creator_default_permissions).toMatchInlineSnapshot(`
|
||||
[{ "id": "team_admin" }]
|
||||
`);
|
||||
});
|
||||
|
||||
@ -926,6 +926,8 @@ export type BranchRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeBran
|
||||
export type EnvironmentRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeEnvironmentConfig<EnvironmentRenderedConfigBeforeSanitization>>>>;
|
||||
export type OrganizationRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeOrganizationConfig>>>;
|
||||
|
||||
// Complete config
|
||||
export type CompleteConfig = OrganizationRenderedConfig;
|
||||
|
||||
// Type assertions (just to make sure the types are correct)
|
||||
const __assertEmptyObjectIsValidProjectOverride: ProjectConfigOverride = {};
|
||||
|
||||
@ -18,7 +18,7 @@ import { StackAdminApp, StackAdminAppConstructorOptions } from "../interfaces/ad
|
||||
import { clientVersion, createCache, getBaseUrl, getDefaultProjectId, getDefaultPublishableClientKey, getDefaultSecretServerKey, getDefaultSuperSecretAdminKey } from "./common";
|
||||
import { _StackServerAppImplIncomplete } from "./server-app-impl";
|
||||
|
||||
import { EnvironmentConfigOverrideOverride, OrganizationRenderedConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { CompleteConfig, EnvironmentConfigOverrideOverride } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { ChatContent } from "@stackframe/stack-shared/dist/interface/admin-interface";
|
||||
import { ConfigCrud } from "@stackframe/stack-shared/dist/interface/crud/config";
|
||||
import { useAsyncCache } from "./common"; // THIS_LINE_PLATFORM react-like
|
||||
@ -87,7 +87,7 @@ export class _StackAdminAppImplIncomplete<HasTokenStore extends boolean, Project
|
||||
});
|
||||
}
|
||||
|
||||
_adminConfigFromCrud(data: ConfigCrud['Admin']['Read']): OrganizationRenderedConfig {
|
||||
_adminConfigFromCrud(data: ConfigCrud['Admin']['Read']): CompleteConfig {
|
||||
return JSON.parse(data.config_string);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ProductionModeError } from "@stackframe/stack-shared/dist/helpers/production-mode";
|
||||
import { AdminUserProjectsCrud, ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
|
||||
|
||||
import { EnvironmentConfigOverrideOverride, OrganizationRenderedConfig } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { CompleteConfig, EnvironmentConfigOverrideOverride } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { StackAdminApp } from "../apps/interfaces/admin-app";
|
||||
import { AdminProjectConfig, AdminProjectConfigUpdateOptions, ProjectConfig } from "../project-configs";
|
||||
|
||||
@ -23,9 +23,9 @@ export type AdminProject = {
|
||||
update(this: AdminProject, update: AdminProjectUpdateOptions): Promise<void>,
|
||||
delete(this: AdminProject): Promise<void>,
|
||||
|
||||
getConfig(this: AdminProject): Promise<OrganizationRenderedConfig>,
|
||||
getConfig(this: AdminProject): Promise<CompleteConfig>,
|
||||
// NEXT_LINE_PLATFORM react-like
|
||||
useConfig(this: AdminProject): OrganizationRenderedConfig,
|
||||
useConfig(this: AdminProject): CompleteConfig,
|
||||
updateConfig(this: AdminProject, config: EnvironmentConfigOverrideOverride): Promise<void>,
|
||||
|
||||
getProductionModeErrors(this: AdminProject): Promise<ProductionModeError[]>,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user