feat: improve instance not found error (#7413)

* feat: improve instance not found error

* unit tests

* check if is templatable

* lint

* assert

* compile tests

* remove error templates

* link to instance not found page

* fmt

* cleanup

* lint
This commit is contained in:
Elio Bischof
2024-02-28 11:49:57 +01:00
committed by GitHub
parent 062d153cfe
commit f4c72cbe14
22 changed files with 55 additions and 38 deletions

View File

@@ -360,7 +360,7 @@ func startAPIs(
http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))), http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))),
) )
limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig) limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig)
apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, limitingAccessInterceptor) apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, config.ExternalDomain, limitingAccessInterceptor)
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating api %w", err) return nil, fmt.Errorf("error creating api %w", err)
} }
@@ -410,7 +410,7 @@ func startAPIs(
if err := apis.RegisterService(ctx, execution_v3_alpha.CreateServer(commands, queries, domain.AllFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil { if err := apis.RegisterService(ctx, execution_v3_alpha.CreateServer(commands, queries, domain.AllFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
return nil, err return nil, err
} }
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, login.IgnoreInstanceEndpoints...) instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, config.ExternalDomain, login.IgnoreInstanceEndpoints...)
assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge) assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge)
apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle)) apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle))

View File

@@ -268,6 +268,8 @@ ZITADEL hosts everything under a single domain: `{instance}.zitadel.cloud` or yo
The domain is used as the OIDC issuer and as the base url for the gRPC and REST APIs, the Login and Console UI, which you'll find under `{your_domain}/ui/console/`. The domain is used as the OIDC issuer and as the base url for the gRPC and REST APIs, the Login and Console UI, which you'll find under `{your_domain}/ui/console/`.
Are you self-hosting and having troubles with *Instance not found* errors? [Check out this page](https://zitadel.com/docs/self-hosting/manage/custom-domain).
## API path prefixes ## API path prefixes
If you run ZITADEL on a custom domain, you may want to reuse that domain for other applications. If you run ZITADEL on a custom domain, you may want to reuse that domain for other applications.

View File

@@ -74,7 +74,8 @@ func New(
queries *query.Queries, queries *query.Queries,
verifier internal_authz.APITokenVerifier, verifier internal_authz.APITokenVerifier,
authZ internal_authz.Config, authZ internal_authz.Config,
tlsConfig *tls.Config, http2HostName, http1HostName string, tlsConfig *tls.Config,
http2HostName, http1HostName, externalDomain string,
accessInterceptor *http_mw.AccessInterceptor, accessInterceptor *http_mw.AccessInterceptor,
) (_ *API, err error) { ) (_ *API, err error) {
api := &API{ api := &API{
@@ -87,7 +88,7 @@ func New(
accessInterceptor: accessInterceptor, accessInterceptor: accessInterceptor,
} }
api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, tlsConfig, accessInterceptor.AccessService()) api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, externalDomain, tlsConfig, accessInterceptor.AccessService())
api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName, accessInterceptor, tlsConfig) api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName, accessInterceptor, tlsConfig)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -14,6 +14,7 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/authz"
zitadel_http "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/i18n" "github.com/zitadel/zitadel/internal/i18n"
"github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
@@ -23,15 +24,15 @@ const (
HTTP1Host = "x-zitadel-http1-host" HTTP1Host = "x-zitadel-http1-host"
) )
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor { func InstanceInterceptor(verifier authz.InstanceVerifier, headerName, externalDomain string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
translator, err := i18n.NewZitadelTranslator(language.English) translator, err := i18n.NewZitadelTranslator(language.English)
logging.OnError(err).Panic("unable to get translator") logging.OnError(err).Panic("unable to get translator")
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
return setInstance(ctx, req, info, handler, verifier, headerName, translator, explicitInstanceIdServices...) return setInstance(ctx, req, info, handler, verifier, headerName, externalDomain, translator, explicitInstanceIdServices...)
} }
} }
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) { func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName, externalDomain string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) {
interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx) interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
for _, service := range idFromRequestsServices { for _, service := range idFromRequestsServices {
@@ -55,16 +56,18 @@ func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInf
return handler(authz.WithInstance(ctx, instance), req) return handler(authz.WithInstance(ctx, instance), req)
} }
} }
host, err := hostFromContext(interceptorCtx, headerName) host, err := hostFromContext(interceptorCtx, headerName)
if err != nil { if err != nil {
return nil, status.Error(codes.NotFound, err.Error()) return nil, status.Error(codes.NotFound, err.Error())
} }
instance, err := verifier.InstanceByHost(interceptorCtx, host) instance, err := verifier.InstanceByHost(interceptorCtx, host)
if err != nil { if err != nil {
notFoundErr := new(zerrors.NotFoundError) err = fmt.Errorf("unable to set instance using origin %s (ExternalDomain is %s): %w", zitadel_http.ComposedOrigin(ctx), externalDomain, err)
if errors.As(err, &notFoundErr) { zErr := new(zerrors.ZitadelError)
notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil) if errors.As(err, &zErr) {
zErr.SetMessage(translator.LocalizeFromCtx(ctx, zErr.GetMessage(), nil))
zErr.Parent = err
err = zErr
} }
return nil, status.Error(codes.NotFound, err.Error()) return nil, status.Error(codes.NotFound, err.Error())
} }

View File

@@ -137,7 +137,7 @@ func Test_setInstance(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := setInstance(tt.args.ctx, tt.args.req, tt.args.info, tt.args.handler, tt.args.verifier, tt.args.headerName, nil) got, err := setInstance(tt.args.ctx, tt.args.req, tt.args.info, tt.args.handler, tt.args.verifier, tt.args.headerName, "", nil)
if (err != nil) != tt.res.err { if (err != nil) != tt.res.err {
t.Errorf("setInstance() error = %v, wantErr %v", err, tt.res.err) t.Errorf("setInstance() error = %v, wantErr %v", err, tt.res.err)
return return

View File

@@ -39,6 +39,7 @@ func CreateServer(
authConfig authz.Config, authConfig authz.Config,
queries *query.Queries, queries *query.Queries,
hostHeaderName string, hostHeaderName string,
externalDomain string,
tlsConfig *tls.Config, tlsConfig *tls.Config,
accessSvc *logstore.Service[*record.AccessLog], accessSvc *logstore.Service[*record.AccessLog],
) *grpc.Server { ) *grpc.Server {
@@ -50,7 +51,7 @@ func CreateServer(
middleware.DefaultTracingServer(), middleware.DefaultTracingServer(),
middleware.MetricsHandler(metricTypes, grpc_api.Probes...), middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
middleware.NoCacheInterceptor(), middleware.NoCacheInterceptor(),
middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName), middleware.InstanceInterceptor(queries, hostHeaderName, externalDomain, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
middleware.AccessStorageInterceptor(accessSvc), middleware.AccessStorageInterceptor(accessSvc),
middleware.ErrorHandler(), middleware.ErrorHandler(),
middleware.LimitsInterceptor(system_pb.SystemService_ServiceDesc.ServiceName), middleware.LimitsInterceptor(system_pb.SystemService_ServiceDesc.ServiceName),

View File

@@ -20,15 +20,16 @@ import (
type instanceInterceptor struct { type instanceInterceptor struct {
verifier authz.InstanceVerifier verifier authz.InstanceVerifier
headerName string headerName, externalDomain string
ignoredPrefixes []string ignoredPrefixes []string
translator *i18n.Translator translator *i18n.Translator
} }
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, ignoredPrefixes ...string) *instanceInterceptor { func InstanceInterceptor(verifier authz.InstanceVerifier, headerName, externalDomain string, ignoredPrefixes ...string) *instanceInterceptor {
return &instanceInterceptor{ return &instanceInterceptor{
verifier: verifier, verifier: verifier,
headerName: headerName, headerName: headerName,
externalDomain: externalDomain,
ignoredPrefixes: ignoredPrefixes, ignoredPrefixes: ignoredPrefixes,
translator: newZitadelTranslator(), translator: newZitadelTranslator(),
} }
@@ -55,9 +56,12 @@ func (a *instanceInterceptor) handleInstance(w http.ResponseWriter, r *http.Requ
} }
ctx, err := setInstance(r, a.verifier, a.headerName) ctx, err := setInstance(r, a.verifier, a.headerName)
if err != nil { if err != nil {
caosErr := new(zerrors.NotFoundError) err = fmt.Errorf("unable to set instance using origin %s (ExternalDomain is %s): %w", zitadel_http.ComposedOrigin(r.Context()), a.externalDomain, err)
if errors.As(err, &caosErr) { zErr := new(zerrors.ZitadelError)
caosErr.Message = a.translator.LocalizeFromRequest(r, caosErr.GetMessage(), nil) if errors.As(err, &zErr) {
zErr.SetMessage(a.translator.LocalizeFromRequest(r, zErr.GetMessage(), nil))
zErr.Parent = err
err = zErr
} }
http.Error(w, err.Error(), http.StatusNotFound) http.Error(w, err.Error(), http.StatusNotFound)
return return
@@ -68,13 +72,13 @@ func (a *instanceInterceptor) handleInstance(w http.ResponseWriter, r *http.Requ
func setInstance(r *http.Request, verifier authz.InstanceVerifier, headerName string) (_ context.Context, err error) { func setInstance(r *http.Request, verifier authz.InstanceVerifier, headerName string) (_ context.Context, err error) {
ctx := r.Context() ctx := r.Context()
authCtx, span := tracing.NewServerInterceptorSpan(ctx) authCtx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
host, err := HostFromRequest(r, headerName) host, err := HostFromRequest(r, headerName)
if err != nil { if err != nil {
return nil, zerrors.ThrowNotFound(err, "INST-zWq7X", "Errors.Instance.NotFound") return nil, zerrors.ThrowNotFound(err, "INST-zWq7X", "Errors.IAM.NotFound")
} }
instance, err := verifier.InstanceByHost(authCtx, host) instance, err := verifier.InstanceByHost(authCtx, host)

View File

@@ -6,6 +6,7 @@ import (
_ "embed" _ "embed"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"strings" "strings"
"time" "time"
@@ -196,7 +197,12 @@ var (
func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Instance, err error) { func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Instance, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() {
if err != nil {
err = fmt.Errorf("unable to get instance by host %s: %w", host, err)
}
span.EndWithError(err)
}()
domain := strings.Split(host, ":")[0] // remove possible port domain := strings.Split(host, ":")[0] // remove possible port
instance, scan := scanAuthzInstance(host, domain) instance, scan := scanAuthzInstance(host, domain)

View File

@@ -330,7 +330,7 @@ Errors:
NotActive: Грантът по проекта не е активен NotActive: Грантът по проекта не е активен
NotInactive: Грантът по проекта не е неактивен NotInactive: Грантът по проекта не е неактивен
IAM: IAM:
NotFound: Екземплярът не е намерен. Вижте https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM не е намерен. Уверете се, че сте получили правилния домейн. Вижте https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Ролите не са сменени RolesNotChanged: Ролите не са сменени
MemberInvalid: Членът е невалиден MemberInvalid: Членът е невалиден

View File

@@ -327,7 +327,7 @@ Errors:
NotActive: Grant projektu není aktivní NotActive: Grant projektu není aktivní
NotInactive: Grant projektu není neaktivní NotInactive: Grant projektu není neaktivní
IAM: IAM:
NotFound: Instance nenalezena. Podívejte se na https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: Instance nebyla nalezena. Ujistěte se, že jste získali správnou doménu. Podívejte se na https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Role nebyly změněny RolesNotChanged: Role nebyly změněny
MemberInvalid: Člen je neplatný MemberInvalid: Člen je neplatný

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: Projekt Grant ist nicht aktiv NotActive: Projekt Grant ist nicht aktiv
NotInactive: Projekt Grant ist nicht inaktiv NotInactive: Projekt Grant ist nicht inaktiv
IAM: IAM:
NotFound: Instanz nicht gefunden. Schau dir https://zitadel.com/docs/self-hosting/manage/custom-domain an NotFound: Instanz nicht gefunden. Stelle sicher, dass Du die richtige Domain hast. Schau unter https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Rollen wurden nicht verändert RolesNotChanged: Rollen wurden nicht verändert
MemberInvalid: Member ist ungültig MemberInvalid: Member ist ungültig

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: Project grant is not active NotActive: Project grant is not active
NotInactive: Project grant is not inactive NotInactive: Project grant is not inactive
IAM: IAM:
NotFound: Instance not found. Check out https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: Instance not found. Make sure you got the domain right. Check out https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Roles have not been changed RolesNotChanged: Roles have not been changed
MemberInvalid: Member is invalid MemberInvalid: Member is invalid

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: La concesión del proyecto no está activa NotActive: La concesión del proyecto no está activa
NotInactive: La concesión del proyecto no está inactiva NotInactive: La concesión del proyecto no está inactiva
IAM: IAM:
NotFound: Instancia no encontrada. Consulta https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: Instancia no encontrada. Asegúrate de que tienes el dominio correcto. Consulta https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Los roles no han cambiado RolesNotChanged: Los roles no han cambiado
MemberInvalid: El miembro no es válido MemberInvalid: El miembro no es válido

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: La subvention de projet n'est pas active NotActive: La subvention de projet n'est pas active
NotInactive: La subvention du projet n'est pas inactive NotInactive: La subvention du projet n'est pas inactive
IAM: IAM:
NotFound: Instance non trouvée. Consultez https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM non trouvé. Assurez-vous que vous avez la bonne organisation. Vérifiez https://zitadel.com/docs/apis/introduction#organizations
Member: Member:
RolesNotChanged: Les rôles n'ont pas été modifiés RolesNotChanged: Les rôles n'ont pas été modifiés
MemberInvalid: Le membre n'est pas valide MemberInvalid: Le membre n'est pas valide

View File

@@ -329,7 +329,7 @@ Errors:
NotActive: Grant del progetto non è attivo NotActive: Grant del progetto non è attivo
NotInactive: Grant del progetto non è inattivo NotInactive: Grant del progetto non è inattivo
IAM: IAM:
NotFound: Istanza non trovata. Controlla https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM non trovato. Assicurati di avere il dominio corretto. Guarda su https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: I ruoli non sono stati cambiati RolesNotChanged: I ruoli non sono stati cambiati
MemberInvalid: Il membro non è valido MemberInvalid: Il membro non è valido

View File

@@ -317,7 +317,7 @@ Errors:
NotActive: プロジェクトグラントはアクティブではありません NotActive: プロジェクトグラントはアクティブではありません
NotInactive: プロジェクトグラントは非アクティブではありません NotInactive: プロジェクトグラントは非アクティブではありません
IAM: IAM:
NotFound: インスタンスが見つかりません https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAMが見つかりません。正しいドメインを持っていることを確認してください。 https://zitadel.com/docs/apis/introduction#domains を参照してください
Member: Member:
RolesNotChanged: ロールは変更されていません RolesNotChanged: ロールは変更されていません
MemberInvalid: 無効なメンバーです MemberInvalid: 無効なメンバーです

View File

@@ -327,7 +327,7 @@ Errors:
NotActive: Овластувањето за проектот не е активно NotActive: Овластувањето за проектот не е активно
NotInactive: Овластувањето за проектот не е неактивно NotInactive: Овластувањето за проектот не е неактивно
IAM: IAM:
NotFound: Инстанцата не е пронајдена. Проверете https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM не е пронајден. Проверете дали имате точен домен. Погледнете на https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Улогите не се променети RolesNotChanged: Улогите не се променети
MemberInvalid: Членот е невалиден MemberInvalid: Членот е невалиден

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: Projecttoekenning is niet actief NotActive: Projecttoekenning is niet actief
NotInactive: Projecttoekenning is niet gedeactiveerd NotInactive: Projecttoekenning is niet gedeactiveerd
IAM: IAM:
NotFound: Instantie niet gevonden. Bekijk https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM niet gevonden. Zorg ervoor dat u het juiste domein heeft. Kijk op https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Rollen zijn niet veranderd RolesNotChanged: Rollen zijn niet veranderd
MemberInvalid: Lid is ongeldig MemberInvalid: Lid is ongeldig

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: Grant projektu jest nieaktywny NotActive: Grant projektu jest nieaktywny
NotInactive: Grant projektu nie jest nieaktywny NotInactive: Grant projektu nie jest nieaktywny
IAM: IAM:
NotFound: Instancja nie znaleziona. Sprawdź https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM nie znaleziony. Upewnij się, że masz poprawną domenę. Sprawdź https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Role nie zmienione RolesNotChanged: Role nie zmienione
MemberInvalid: Członek jest nieprawidłowy MemberInvalid: Członek jest nieprawidłowy

View File

@@ -326,7 +326,7 @@ Errors:
NotActive: A concessão do projeto não está ativa NotActive: A concessão do projeto não está ativa
NotInactive: A concessão do projeto não está inativa NotInactive: A concessão do projeto não está inativa
IAM: IAM:
NotFound: Instância não encontrada. Confira https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM não encontrado. Verifique se você tem o domínio correto. Consulte https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: As funções não foram alteradas RolesNotChanged: As funções não foram alteradas
MemberInvalid: O membro é inválido MemberInvalid: O membro é inválido

View File

@@ -321,7 +321,7 @@ Errors:
NotActive: Грант проекта не активен NotActive: Грант проекта не активен
NotInactive: Грант проекта не неактивен NotInactive: Грант проекта не неактивен
IAM: IAM:
NotFound: Экземпляр не найден. Проверьте https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM не найден. Убедитесь, что у вас есть правильный домен. Смотрите на https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: Роли не изменились RolesNotChanged: Роли не изменились
MemberInvalid: Участник недействителен MemberInvalid: Участник недействителен

View File

@@ -328,7 +328,7 @@ Errors:
NotActive: 项目授权不是启用状态 NotActive: 项目授权不是启用状态
NotInactive: 项目授权不是停用状态 NotInactive: 项目授权不是停用状态
IAM: IAM:
NotFound: 实例未找到。查看 https://zitadel.com/docs/self-hosting/manage/custom-domain NotFound: IAM 未找到。确保您有正确的域。查看 https://zitadel.com/docs/apis/introduction#domains
Member: Member:
RolesNotChanged: 角色没有改变 RolesNotChanged: 角色没有改变
MemberInvalid: 成员无效 MemberInvalid: 成员无效