mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-12 00:55:12 +00:00
chore: standardised the eslint config structure accross the project (#11814)
This commit is contained in:
@@ -13,30 +13,21 @@ interface AvatarProps {
|
||||
}
|
||||
|
||||
export function getInitials(name: string, loginName: string) {
|
||||
let credentials = "";
|
||||
if (name) {
|
||||
const split = name.split(" ");
|
||||
if (split) {
|
||||
const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
credentials = initials;
|
||||
} else {
|
||||
credentials = name.charAt(0);
|
||||
}
|
||||
} else {
|
||||
const username = loginName.split("@")[0];
|
||||
let separator = "_";
|
||||
if (username.includes("-")) {
|
||||
separator = "-";
|
||||
}
|
||||
if (username.includes(".")) {
|
||||
separator = ".";
|
||||
}
|
||||
const split = username.split(separator);
|
||||
const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
credentials = initials;
|
||||
return split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
}
|
||||
|
||||
return credentials;
|
||||
const username = loginName.split("@")[0];
|
||||
let separator = "_";
|
||||
if (username.includes("-")) {
|
||||
separator = "-";
|
||||
}
|
||||
if (username.includes(".")) {
|
||||
separator = ".";
|
||||
}
|
||||
const split = username.split(separator);
|
||||
return split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
}
|
||||
|
||||
// Helper function to get avatar roundness from theme
|
||||
|
||||
@@ -65,7 +65,6 @@ function getDefaultButtonAppearance(): string {
|
||||
return appearance?.button || "border border-button-light-border dark:border-button-dark-border"; // Fallback to flat design
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ function getDefaultCardStyling(): string {
|
||||
return appearance?.card || "shadow-sm border-0"; // Fallback to material design
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Card = forwardRef<HTMLDivElement, CardProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
||||
checked={enabled}
|
||||
onChange={(event) => {
|
||||
setEnabled(event.target?.checked);
|
||||
onChangeVal && onChangeVal(event.target?.checked);
|
||||
if (onChangeVal) onChangeVal(event.target?.checked);
|
||||
}}
|
||||
disabled={disabled}
|
||||
type="checkbox"
|
||||
|
||||
@@ -17,7 +17,7 @@ export function CopyToClipboard({ value }: Props) {
|
||||
const to = setTimeout(setCopied, 1000, false);
|
||||
return () => clearTimeout(to);
|
||||
}
|
||||
}, [copied]);
|
||||
}, [copied, value]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center px-2">
|
||||
|
||||
@@ -91,7 +91,7 @@ export function IdpProcessHandler({
|
||||
setError(err instanceof Error ? err.message : t("processing.unexpectedError"));
|
||||
setLoading(false);
|
||||
});
|
||||
}, [provider, id, token, requestId, organization, link, sessionId, postErrorRedirectUrl, router]);
|
||||
}, [provider, id, token, requestId, organization, link, sessionId, linkFingerprint, postErrorRedirectUrl, router, t]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
|
||||
@@ -37,7 +37,6 @@ function getDefaultInputRoundness(): string {
|
||||
return getComponentRoundness("input");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Button, ButtonVariants } from "./button";
|
||||
import { Spinner } from "./spinner";
|
||||
import { Translated } from "./translated";
|
||||
|
||||
type Inputs = {};
|
||||
type Inputs = Record<string, never>;
|
||||
|
||||
type Props = {
|
||||
sessionId?: string;
|
||||
@@ -44,6 +44,35 @@ export function RegisterPasskey({
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const continueAndLogin = useCallback(
|
||||
(loginName?: string) => {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (requestId) {
|
||||
params.set("requestId", requestId);
|
||||
}
|
||||
|
||||
if (sessionId) {
|
||||
params.set("sessionId", sessionId);
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (loginName ?? initialLoginName) {
|
||||
params.set("loginName", loginName ?? initialLoginName ?? "");
|
||||
}
|
||||
|
||||
router.push("/passkey?" + params);
|
||||
},
|
||||
[organization, requestId, sessionId, userId, initialLoginName, router],
|
||||
);
|
||||
|
||||
async function submitVerify(
|
||||
passkeyId: string,
|
||||
passkeyName: string,
|
||||
@@ -166,7 +195,7 @@ export function RegisterPasskey({
|
||||
}
|
||||
|
||||
continueAndLogin(verificationResponse.loginName);
|
||||
}, [sessionId, userId, code]);
|
||||
}, [sessionId, userId, code, codeId, continueAndLogin]);
|
||||
|
||||
// Auto-submit when code is provided (similar to VerifyForm)
|
||||
useEffect(() => {
|
||||
@@ -175,32 +204,6 @@ export function RegisterPasskey({
|
||||
}
|
||||
}, [code, submitRegisterAndContinue]);
|
||||
|
||||
function continueAndLogin(loginName?: string) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (requestId) {
|
||||
params.set("requestId", requestId);
|
||||
}
|
||||
|
||||
if (sessionId) {
|
||||
params.set("sessionId", sessionId);
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (loginName ?? initialLoginName) {
|
||||
params.set("loginName", loginName ?? initialLoginName ?? "");
|
||||
}
|
||||
|
||||
router.push("/passkey?" + params);
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="w-full">
|
||||
{error && (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { clearSession } from "@/lib/server/session";
|
||||
import { timestampDate } from "@zitadel/client";
|
||||
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Alert, AlertType } from "./alert";
|
||||
import { SessionClearItem } from "./session-clear-item";
|
||||
import { Translated } from "./translated";
|
||||
@@ -20,7 +20,7 @@ export function SessionsClearList({ sessions, logoutHint, postLogoutRedirectUri,
|
||||
const [list, setList] = useState<Session[]>(sessions);
|
||||
const router = useRouter();
|
||||
|
||||
async function clearHintedSession() {
|
||||
const clearHintedSession = useCallback(async () => {
|
||||
console.log("Clearing session for login hint:", logoutHint);
|
||||
// If a login hint is provided, we logout that specific session
|
||||
const sessionIdToBeCleared = sessions.find((session) => {
|
||||
@@ -53,13 +53,13 @@ export function SessionsClearList({ sessions, logoutHint, postLogoutRedirectUri,
|
||||
} else {
|
||||
console.warn(`No session found for login hint: ${logoutHint}`);
|
||||
}
|
||||
}
|
||||
}, [logoutHint, sessions, postLogoutRedirectUri, organization, router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (logoutHint) {
|
||||
clearHintedSession();
|
||||
}
|
||||
}, []);
|
||||
}, [logoutHint, clearHintedSession]);
|
||||
|
||||
return sessions ? (
|
||||
<div className="flex flex-col space-y-2">
|
||||
|
||||
@@ -5,7 +5,7 @@ import { sendLoginname } from "@/lib/server/loginname";
|
||||
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Alert } from "./alert";
|
||||
import { AutoSubmitForm } from "./auto-submit-form";
|
||||
@@ -55,34 +55,37 @@ export function UsernameForm({
|
||||
const [error, setError] = useState<string>("");
|
||||
const [samlData, setSamlData] = useState<{ url: string; fields: Record<string, string> } | null>(null);
|
||||
|
||||
async function submitLoginName(values: Inputs, organization?: string) {
|
||||
setLoading(true);
|
||||
const submitLoginName = useCallback(
|
||||
async (values: Inputs, organization?: string) => {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const res = await sendLoginname({
|
||||
loginName: values.loginName,
|
||||
organization,
|
||||
defaultOrganization,
|
||||
requestId,
|
||||
suffix,
|
||||
ignoreUnknownUsernames: loginSettings?.ignoreUnknownUsernames,
|
||||
});
|
||||
try {
|
||||
const res = await sendLoginname({
|
||||
loginName: values.loginName,
|
||||
organization,
|
||||
defaultOrganization,
|
||||
requestId,
|
||||
suffix,
|
||||
ignoreUnknownUsernames: loginSettings?.ignoreUnknownUsernames,
|
||||
});
|
||||
|
||||
handleServerActionResponse(res, router, setSamlData, setError);
|
||||
return res;
|
||||
} catch {
|
||||
setError(t("errors.internalError"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
handleServerActionResponse(res, router, setSamlData, setError);
|
||||
return res;
|
||||
} catch {
|
||||
setError(t("errors.internalError"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[defaultOrganization, requestId, suffix, loginSettings, router, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (submit && loginName) {
|
||||
// When we navigate to this page, we always want to be redirected if submit is true and the parameters are valid.
|
||||
submitLoginName({ loginName }, organization);
|
||||
}
|
||||
}, []);
|
||||
}, [submit, loginName, organization, submitLoginName]);
|
||||
|
||||
let inputLabel = t("labels.loginname");
|
||||
if (loginSettings?.disableLoginWithEmail && loginSettings?.disableLoginWithPhone) {
|
||||
|
||||
@@ -101,7 +101,7 @@ export function VerifyForm({ userId, loginName, organization, requestId, code, i
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[isInvite, userId],
|
||||
[isInvite, userId, loginName, organization, requestId, router, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -341,58 +341,39 @@ export function hashCode(str: string, seed = 0): number {
|
||||
|
||||
export function getMembershipColor(role: string): ColorShade {
|
||||
const hash = hashCode(role);
|
||||
let color = COLORS[hash % COLORS.length];
|
||||
|
||||
switch (role) {
|
||||
case "IAM_OWNER":
|
||||
color = COLORS[0];
|
||||
break;
|
||||
return COLORS[0];
|
||||
case "IAM_OWNER_VIEWER":
|
||||
color = COLORS[14];
|
||||
break;
|
||||
return COLORS[14];
|
||||
case "IAM_ORG_MANAGER":
|
||||
color = COLORS[11];
|
||||
break;
|
||||
return COLORS[11];
|
||||
case "IAM_USER_MANAGER":
|
||||
color = COLORS[8];
|
||||
break;
|
||||
return COLORS[8];
|
||||
|
||||
case "ORG_OWNER":
|
||||
color = COLORS[16];
|
||||
break;
|
||||
return COLORS[16];
|
||||
case "ORG_USER_MANAGER":
|
||||
color = COLORS[8];
|
||||
break;
|
||||
return COLORS[8];
|
||||
case "ORG_OWNER_VIEWER":
|
||||
color = COLORS[14];
|
||||
break;
|
||||
return COLORS[14];
|
||||
case "ORG_USER_PERMISSION_EDITOR":
|
||||
color = COLORS[7];
|
||||
break;
|
||||
return COLORS[7];
|
||||
case "ORG_PROJECT_PERMISSION_EDITOR":
|
||||
color = COLORS[11];
|
||||
break;
|
||||
return COLORS[11];
|
||||
case "ORG_PROJECT_CREATOR":
|
||||
color = COLORS[12];
|
||||
break;
|
||||
return COLORS[12];
|
||||
|
||||
case "PROJECT_OWNER":
|
||||
color = COLORS[9];
|
||||
break;
|
||||
return COLORS[9];
|
||||
case "PROJECT_OWNER_VIEWER":
|
||||
color = COLORS[10];
|
||||
break;
|
||||
return COLORS[10];
|
||||
case "PROJECT_OWNER_GLOBAL":
|
||||
color = COLORS[11];
|
||||
break;
|
||||
return COLORS[11];
|
||||
case "PROJECT_OWNER_VIEWER_GLOBAL":
|
||||
color = COLORS[12];
|
||||
break;
|
||||
return COLORS[12];
|
||||
|
||||
default:
|
||||
color = COLORS[hash % COLORS.length];
|
||||
break;
|
||||
return COLORS[hash % COLORS.length];
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export default getRequestConfig(async () => {
|
||||
|
||||
const i18nOrganization = _headers.get("x-zitadel-i18n-organization") || ""; // You may need to set this header in middleware
|
||||
|
||||
let translations: JsonObject | {} = {};
|
||||
let translations: JsonObject | Record<string, never> = {};
|
||||
try {
|
||||
const i18nJSON = await getHostedLoginTranslation({
|
||||
serviceConfig,
|
||||
|
||||
@@ -46,6 +46,8 @@ export async function loginServiceKeyToken() {
|
||||
key: key,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to read login service key file "${keyFile}": ${err instanceof Error ? err.message : err}`);
|
||||
throw new Error(`Failed to read login service key file "${keyFile}": ${err instanceof Error ? err.message : err}`, {
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,24 +374,26 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
};
|
||||
}
|
||||
|
||||
const paramsPassword = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
{
|
||||
const paramsPassword = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
|
||||
if (organization) {
|
||||
paramsPassword.append("organization", organization);
|
||||
if (organization) {
|
||||
paramsPassword.append("organization", organization);
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
paramsPassword.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: "/password?" + paramsPassword,
|
||||
};
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
paramsPassword.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: "/password?" + paramsPassword,
|
||||
};
|
||||
|
||||
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
|
||||
if (userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED || !userLoginSettings?.allowLocalAuthentication) {
|
||||
if (command.ignoreUnknownUsernames) {
|
||||
@@ -402,22 +404,24 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
};
|
||||
}
|
||||
|
||||
const paramsPasskey = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
if (command.requestId) {
|
||||
paramsPasskey.append("requestId", command.requestId);
|
||||
{
|
||||
const paramsPasskey = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
if (command.requestId) {
|
||||
paramsPasskey.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
paramsPasskey.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: "/passkey?" + paramsPasskey };
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
paramsPasskey.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: "/passkey?" + paramsPasskey };
|
||||
|
||||
case AuthenticationMethodType.IDP:
|
||||
case AuthenticationMethodType.IDP: {
|
||||
const resp = await redirectUserToIDP(userId, organization);
|
||||
|
||||
if (resp?.error) {
|
||||
@@ -425,6 +429,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// prefer passkey in favor of other methods
|
||||
|
||||
@@ -317,7 +317,6 @@ describe("sendPassword", () => {
|
||||
const { completeFlowOrGetUrl } = await import("../client");
|
||||
vi.mocked(completeFlowOrGetUrl).mockResolvedValue({ redirect: "https://example.com" });
|
||||
|
||||
// eslint-disable-next-line
|
||||
const verifyHelper = await import("../verify-helper");
|
||||
vi.mocked(verifyHelper.checkPasswordChangeRequired).mockReturnValue(undefined);
|
||||
vi.mocked(verifyHelper.checkEmailVerification).mockReturnValue(undefined);
|
||||
|
||||
@@ -293,7 +293,7 @@ export async function sendPassword(
|
||||
return {
|
||||
error: t(messageKey, {
|
||||
failedAttempts: error.failedAttempts,
|
||||
maxPasswordAttempts: hasLimit ? (lockoutSettings?.maxPasswordAttempts).toString() : "?",
|
||||
maxPasswordAttempts: hasLimit ? String(lockoutSettings?.maxPasswordAttempts ?? 0) : "?",
|
||||
lockoutMessage: locked ? t("errors.accountLockedContactAdmin") : "",
|
||||
}),
|
||||
};
|
||||
@@ -565,7 +565,7 @@ export async function checkSessionAndSetPassword({
|
||||
return {
|
||||
error: t(messageKey, {
|
||||
failedAttempts: error.failedAttempts,
|
||||
maxPasswordAttempts: hasLimit ? (lockoutSettings?.maxPasswordAttempts).toString() : "?",
|
||||
maxPasswordAttempts: hasLimit ? String(lockoutSettings?.maxPasswordAttempts ?? 0) : "?",
|
||||
lockoutMessage: locked ? t("errors.accountLockedContactAdmin") : "",
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -164,6 +164,7 @@ export async function checkMFAFactors(
|
||||
|
||||
// TODO: provide a way to setup passkeys on mfa page?
|
||||
return { redirect: `/mfa/set?` + params };
|
||||
/* eslint-disable no-dupe-else-if -- TODO: this branch is unreachable, conditions overlap with the previous branch */
|
||||
} else if (
|
||||
loginSettings?.mfaInitSkipLifetime &&
|
||||
(loginSettings.mfaInitSkipLifetime.nanos > 0 || loginSettings.mfaInitSkipLifetime.seconds > 0) &&
|
||||
@@ -171,6 +172,7 @@ export async function checkMFAFactors(
|
||||
session?.factors?.user?.id &&
|
||||
shouldEnforceMFA(session, loginSettings)
|
||||
) {
|
||||
/* eslint-enable no-dupe-else-if */
|
||||
const userResponse = await getUserByID({ serviceConfig, userId: session.factors?.user?.id });
|
||||
|
||||
const humanUser = userResponse?.user?.type.case === "human" ? userResponse?.user.type.value : undefined;
|
||||
|
||||
@@ -1237,20 +1237,18 @@ export interface ServiceConfig {
|
||||
/**
|
||||
* Base type that all function parameters must extend to ensure serviceConfig is always required
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export type WithServiceConfig<T = {}> = T & {
|
||||
serviceConfig: ServiceConfig;
|
||||
};
|
||||
|
||||
export function createServerTransport(token: string, serviceConfig: ServiceConfig) {
|
||||
// Build interceptors list - OTEL interceptor is always included for trace propagation
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const interceptors: any[] = [otelGrpcInterceptor];
|
||||
|
||||
// Add custom headers interceptor if needed
|
||||
if (process.env.CUSTOM_REQUEST_HEADERS || serviceConfig.instanceHost || serviceConfig.publicHost) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
interceptors.push((next: any) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (req: any) => {
|
||||
// Apply headers from serviceConfig
|
||||
if (serviceConfig.instanceHost) {
|
||||
|
||||
Reference in New Issue
Block a user