@greatapps/common 1.1.778 → 1.1.780
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/account/DisableTwoFactorAuthModal.mjs +40 -43
- package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -1
- package/dist/components/account/TwoFactorAuthModal.mjs +123 -129
- package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -1
- package/dist/components/account/sections/PreferencesSection.mjs +3 -1
- package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
- package/dist/components/modals/ModalManager.mjs.map +1 -1
- package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs +8 -11
- package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs.map +1 -1
- package/dist/hooks/useDomMutationGuardHit.mjs +36 -0
- package/dist/hooks/useDomMutationGuardHit.mjs.map +1 -0
- package/dist/i18n/resolve-locale.mjs +10 -0
- package/dist/i18n/resolve-locale.mjs.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/infra/api/client.mjs +10 -0
- package/dist/infra/api/client.mjs.map +1 -1
- package/dist/modules/auth/services/auth.service.mjs +12 -6
- package/dist/modules/auth/services/auth.service.mjs.map +1 -1
- package/dist/utils/dom/dom-mutation-guard.mjs +20 -0
- package/dist/utils/dom/dom-mutation-guard.mjs.map +1 -0
- package/dist/utils/intl/locales.mjs +6 -1
- package/dist/utils/intl/locales.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/account/DisableTwoFactorAuthModal.tsx +0 -1
- package/src/components/account/TwoFactorAuthModal.tsx +0 -2
- package/src/components/account/sections/PreferencesSection.tsx +3 -2
- package/src/components/modals/ModalManager.tsx +0 -4
- package/src/components/widgets/ImageUpload/ImageTooSmallModal.tsx +0 -1
- package/src/hooks/useDomMutationGuardHit.ts +55 -0
- package/src/i18n/resolve-locale.ts +19 -4
- package/src/index.ts +3 -0
- package/src/infra/api/client.ts +41 -0
- package/src/modules/auth/services/auth.service.ts +14 -6
- package/src/utils/dom/dom-mutation-guard.ts +46 -0
- package/src/utils/intl/locales.ts +7 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/auth/services/auth.service.ts"],"sourcesContent":["import { cookies } from \"next/headers\";\n\nimport { api, type RequestConfig } from \"../../../infra/api/client\";\nimport { ApiError } from \"../../../infra/api/types\";\nimport { whitelabelService } from \"../../whitelabel/services/whitelabel.service\";\nimport { buildWlOverrideFromJwt } from \"../utils/build-wl-override\";\nimport {\n ClientInfo,\n ForgotPasswordRequest,\n ForgotPasswordResponse,\n GeoLocation,\n LoginApiResponse,\n LoginRequest,\n LoginResponse,\n LogoutApiResponse,\n LogoutRequest,\n LogoutResponse,\n RegisterApiRequest,\n RegisterApiResponse,\n RegisterRequest,\n RegisterResponse,\n ResendVerificationRequest,\n ResendVerificationResponse,\n ResetPasswordRequest,\n ResetPasswordResponse,\n SearchPlansApiResponse,\n SessionKeepRequest,\n SessionKeepResponse,\n SocialGoogleApiResponse,\n SocialGoogleResponse,\n SocialOnboardingApiResponse,\n SocialOnboardingRequest,\n SocialOnboardingResponse,\n IsolatedLoginResponse,\n TwoFactorApiResponse,\n TwoFactorRequest,\n TwoFactorResponse,\n UnlinkGoogleApiResponse,\n UnlinkGoogleResponse,\n VerifyEmailRequest,\n VerifyEmailResponse,\n} from \"../schema\";\nimport { findWhitelabel } from \"../../../server\";\n\nconst AUTH_COOKIE_NAME = \"greatapps\";\nconst COOKIE_MAX_AGE = 60 * 60 * 24 * 30;\n\n/** Plano Free (ID fixo). */\nconst FREE_PLAN_ID = 5;\n\n/**\n * O cadastro pede o Free de duas formas, e as duas valem: ausência de plano (que o\n * `resolvePlanId` traduz para o sentinela -1) e o id do próprio Free.\n *\n * Olhar só o sentinela fazia `?id_plan=5` cair no ramo `trial` e criar um trial de 7 dias\n * do plano gratuito, com `date_due` e `trial_days` — estado que não existe no produto.\n */\nfunction isFreePlan(idPlan: number): boolean {\n return idPlan === -1 || idPlan === FREE_PLAN_ID;\n}\n\nconst COOKIE_OPTIONS = {\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\" as const,\n path: \"/\",\n};\n\nclass AuthService {\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n ): Promise<LoginResponse>;\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n options: { setCookie: false },\n ): Promise<IsolatedLoginResponse>;\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n options?: { setCookie?: boolean },\n ): Promise<LoginResponse | IsolatedLoginResponse>;\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n options?: { setCookie?: boolean },\n ): Promise<LoginResponse | IsolatedLoginResponse> {\n let requestConfig: RequestConfig | undefined;\n if (credentials.id_wl) {\n const wlToken = await whitelabelService.getTokenByWhitelabelId(\n credentials.id_wl,\n );\n requestConfig = { whiteLabelId: credentials.id_wl, authToken: wlToken };\n }\n\n const response = await api.apps.post<LoginApiResponse>(\n \"/auth/login\",\n {\n email: credentials.email,\n password: credentials.password,\n source: credentials?.source,\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n },\n requestConfig,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"E-mail ou senha incorretos\",\n response.code || \"LOGIN_FAILED\",\n 401,\n );\n }\n\n if (!response.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n if (response.two_factor_required) {\n return { result: \"two_factor_required\", cookie: response.cookie, twoFactorMode: response.two_factor_required };\n }\n\n // Modo isolado: caller só quer o token, sem afetar a sessão do navegador\n // nem pagar pelas chamadas de user/account (ex.: fluxo de SSO).\n if (options?.setCookie === false) {\n return { result: \"success\", accessToken: response.cookie };\n }\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(\n requestConfig ? { authToken: requestConfig.authToken } : undefined,\n ),\n accountService.findCurrentAccount(requestConfig),\n ]);\n\n return {\n result: \"success\",\n user,\n account,\n accessToken: response.cookie,\n refreshToken: \"\",\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n };\n }\n\n async verifyTwoFactor(\n cookie: string,\n code: string,\n clientInfo: ClientInfo,\n options?: { window?: number; setCookie?: boolean },\n ): Promise<TwoFactorResponse> {\n const payload: TwoFactorRequest = {\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n cookie,\n code,\n };\n\n const endpoint = options?.window !== undefined ? `/auth/code?window=${options.window}` : '/auth/code';\n const response = await api.apps.post<TwoFactorApiResponse>(\n endpoint,\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\"Código 2FA inválido\", \"TWO_FACTOR_FAILED\", 401);\n }\n\n if (!response?.data?.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida após 2FA\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n // Modo isolado: devolve o token sem gravar o cookie — o caller redireciona\n // pra outro lugar (ex.: SSO callback) e não quer mexer na sessão atual.\n if (options?.setCookie === false) {\n return { status: 1, accessToken: response.data.cookie };\n }\n\n await this.setAuthCookie(response.data.cookie);\n\n return { status: 1 };\n }\n\n async socialLoginGoogle(\n code: string,\n clientInfo: ClientInfo,\n ): Promise<SocialGoogleResponse> {\n const response = await api.apps.post<SocialGoogleApiResponse>(\n \"/auth/social/google\",\n {\n code,\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n verification: true,\n },\n );\n\n if (response.status === 0) {\n if (response.code === \"link_requires_password\") {\n return {\n result: \"link_requires_password\",\n message:\n response.message ||\n \"Uma conta com esse e-mail já existe. Faça login com e-mail e senha para vincular sua conta Google.\",\n };\n }\n throw new ApiError(\n response.message || \"Falha no login com Google\",\n response.code || \"GOOGLE_LOGIN_FAILED\",\n 401,\n );\n }\n\n if (response.needs_onboarding) {\n if (!response.onboarding_token || !response.partial?.email) {\n throw new ApiError(\n \"Resposta de onboarding inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n return {\n result: \"needs_onboarding\",\n onboardingToken: response.onboarding_token,\n partial: response.partial,\n };\n }\n\n if (!response.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n if (response.two_factor_required) {\n return {\n result: \"two_factor_required\",\n cookie: response.cookie,\n twoFactorMode: response.two_factor_required,\n };\n }\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(),\n accountService.findCurrentAccount(),\n ]);\n\n return {\n result: \"success\",\n user,\n account,\n accessToken: response.cookie,\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n };\n }\n\n async completeGoogleOnboarding(\n data: SocialOnboardingRequest,\n clientInfo: ClientInfo,\n ): Promise<SocialOnboardingResponse> {\n const today = new Date();\n const trialEndDate = new Date(today);\n const trialDays = Math.min(Math.max(data.trialDays ?? 7, 1), 90);\n trialEndDate.setDate(today.getDate() + trialDays);\n\n const idPlan = await this.resolvePlanId(data.idPlan);\n\n const payload = {\n onboarding_token: data.onboardingToken,\n name: data.user.name,\n bussiness_type: data.businessType ?? 0,\n language: \"pt-br\",\n timezone: \"America/Sao_Paulo\",\n currency: \"BRL\",\n location: clientInfo.location,\n ip: clientInfo.ip,\n agent: clientInfo.agent,\n id_affiliate: data.affiliateId || 0,\n origin: data.origin,\n user: {\n id_api: \"\",\n name: data.user.name || \"\",\n last_name: data.user.last_name || \"\",\n rg: data.user.rg || \"\",\n cpf: data.user.cpf || \"\",\n gender: data.user.gender ?? 1,\n ddi: data.user.ddi || \"55\",\n phone: data.user.phone || \"\",\n profile: \"owner\",\n language: \"pt-br\",\n },\n subscription:\n isFreePlan(idPlan)\n ? {\n type: \"free\",\n id_coupon: data.couponId || 0,\n id_plan: FREE_PLAN_ID,\n id_product: 1,\n }\n : {\n type: \"trial\",\n id_coupon: data.couponId || 0,\n id_plan: idPlan,\n date_due: trialEndDate.toISOString().split(\"T\")[0],\n trial_days: trialDays,\n id_product: 1,\n },\n };\n\n const response = await api.apps.post<SocialOnboardingApiResponse>(\n \"/auth/social/onboarding\",\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao finalizar cadastro com Google\",\n \"ONBOARDING_FAILED\",\n 400,\n );\n }\n\n if (!response.data?.length || !response.cookie) {\n throw new ApiError(\n \"Resposta de cadastro inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(),\n accountService.findCurrentAccount(),\n ]);\n\n return {\n user,\n account,\n accessToken: response.cookie,\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n };\n }\n\n async unlinkGoogle(): Promise<UnlinkGoogleResponse> {\n const { id_account, id_user } = await import(\n \"../utils/get-user-context\"\n ).then((m) => m.getUserContext());\n\n const response = await api.apps.delete<UnlinkGoogleApiResponse>(\n `/accounts/${id_account}/users/${id_user}/social/google`,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao desvincular conta Google\",\n \"UNLINK_FAILED\",\n 400,\n );\n }\n\n return {\n success: true,\n message: response.message,\n };\n }\n\n async register(\n data: RegisterRequest,\n clientInfo: ClientInfo,\n ): Promise<RegisterResponse> {\n const today = new Date();\n const trialEndDate = new Date(today);\n const trialDays = Math.min(Math.max(data.trialDays ?? 7, 1), 90);\n trialEndDate.setDate(today.getDate() + trialDays);\n\n const idPlan = await this.resolvePlanId(data.idPlan);\n\n const payload: RegisterApiRequest = {\n name: data.accountName,\n bussiness_type: data.businessType ?? 0,\n language: \"pt-br\",\n timezone: \"America/Sao_Paulo\",\n currency: \"BRL\",\n location: clientInfo.location,\n ip: clientInfo.ip,\n agent: clientInfo.agent,\n id_affiliate: data.affiliateId || 0,\n origin: data.origin,\n user: {\n id_api: \"\",\n name: data.name,\n last_name: data.lastName || \"\",\n rg: data.rg || \"\",\n cpf: data.cpf || \"\",\n gender: data.gender,\n email: data.email,\n phone: data.phone,\n password: data.password,\n profile: \"owner\",\n language: \"pt-br\",\n },\n subscription:\n isFreePlan(idPlan)\n ? {\n type: \"free\",\n id_coupon: data.couponId || 0,\n id_plan: FREE_PLAN_ID,\n id_product: 1,\n }\n : {\n type: \"trial\",\n id_coupon: data.couponId || 0,\n id_plan: idPlan,\n // Fluxo não-Stripe lê `date_due`; fluxo Stripe lê `trial_days`.\n // Mandamos os dois pra cobrir ambos os caminhos da API.\n date_due: trialEndDate.toISOString().split(\"T\")[0],\n trial_days: trialDays,\n id_product: 1,\n },\n };\n\n const response = await api.apps.post<RegisterApiResponse>(\n \"/accounts\",\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao criar conta\",\n \"REGISTER_FAILED\",\n 400,\n );\n }\n\n if (!response.data || response.data.length === 0) {\n throw new ApiError(\n \"Resposta de registro inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n if (!response.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n const accountData = response.data[0];\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(),\n accountService.findCurrentAccount(),\n ]);\n\n return {\n user,\n account,\n accessToken: response.cookie,\n refreshToken: \"\",\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n requiresEmailVerification: !accountData.verified,\n };\n }\n\n async logout(clientInfo: ClientInfo): Promise<LogoutResponse> {\n const cookie = await this.getToken();\n\n if (!cookie) {\n throw new ApiError(\n \"Usuário não autenticado\",\n \"NOT_AUTHENTICATED_LOGOUT\",\n 401\n );\n }\n\n const payload: LogoutRequest = {\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n cookie,\n };\n\n try {\n const response = await api.apps.post<LogoutApiResponse>(\n \"/auth/logout\",\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao realizar logout\",\n response.code || \"LOGOUT_FAILED\",\n 400,\n );\n }\n } finally {\n await this.removeAuthCookie();\n }\n\n return {\n success: true,\n };\n }\n\n async forgotPassword(\n _data: ForgotPasswordRequest,\n ): Promise<ForgotPasswordResponse> {\n throw new ApiError(\n \"Recuperação de senha não implementada\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async resetPassword(\n _data: ResetPasswordRequest,\n ): Promise<ResetPasswordResponse> {\n throw new ApiError(\n \"Redefinição de senha não implementada\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async verifyEmail(_data: VerifyEmailRequest): Promise<VerifyEmailResponse> {\n throw new ApiError(\n \"Verificação de email não implementada\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async resendVerification(\n _data: ResendVerificationRequest,\n ): Promise<ResendVerificationResponse> {\n throw new ApiError(\n \"Reenvio de verificação não implementado\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async validateSession(clientInfo: ClientInfo): Promise<boolean> {\n const cookie = await this.getToken();\n\n if (!cookie) {\n return false;\n }\n\n try {\n const requestConfig = await buildWlOverrideFromJwt(cookie);\n\n const payload: SessionKeepRequest = {\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n cookie,\n };\n\n const response = await api.apps.post<SessionKeepResponse>(\n \"/auth/keep\",\n payload,\n requestConfig,\n );\n\n return response.status === 1;\n } catch (error) {\n console.error(\"[AuthService] validateSession error\", error);\n return false;\n }\n }\n\n async isAuthenticated(): Promise<boolean> {\n const token = await this.getToken();\n return !!token;\n }\n\n async getToken(): Promise<string | undefined> {\n if (process.env.DUMMY_AUTH_TOKEN) return process.env.DUMMY_AUTH_TOKEN;\n const cookieStore = await cookies();\n return cookieStore.get(AUTH_COOKIE_NAME)?.value;\n }\n\n private async resolvePlanId(idPlan?: number | string | null): Promise<number> {\n if (idPlan == null || idPlan === \"\") return -1;\n if (typeof idPlan === \"number\" || Number(idPlan)) return Number(idPlan);\n\n if (typeof idPlan === \"string\") {\n const response = await api.apps.get<SearchPlansApiResponse>(\n `/plans?search=${encodeURIComponent(idPlan)}`,\n );\n\n if (response.status === 1 && response.data?.length) {\n return response.data[0].id;\n }\n\n throw new ApiError(\n `Plano \"${idPlan}\" não encontrado`,\n \"PLAN_NOT_FOUND\",\n 404,\n );\n }\n return -1;\n }\n\n private async setAuthCookie(token: string): Promise<void> {\n const cookieStore = await cookies();\n const whitelabel = await findWhitelabel().catch(() => null);\n const cookieDomain =\n this.normalizeCookieDomain(whitelabel?.domain);\n\n cookieStore.set(AUTH_COOKIE_NAME, token, {\n ...COOKIE_OPTIONS,\n ...(cookieDomain ? { domain: cookieDomain } : {}),\n maxAge: COOKIE_MAX_AGE,\n });\n }\n\n async removeAuthCookie(): Promise<void> {\n const cookieStore = await cookies();\n const whitelabel = await findWhitelabel().catch(() => null);\n const cookieDomain =\n this.normalizeCookieDomain(whitelabel?.domain);\n\n cookieStore.delete({\n name: AUTH_COOKIE_NAME,\n ...COOKIE_OPTIONS,\n ...(cookieDomain ? { domain: cookieDomain } : {}),\n });\n }\n\n private normalizeCookieDomain(domain?: string | null): string | undefined {\n if (!domain) return undefined;\n\n const normalized = domain.trim();\n if (!normalized) return undefined;\n\n const rawHost = (() => {\n try {\n return new URL(normalized).hostname;\n } catch {\n return normalized\n .replace(/^https?:\\/\\//i, \"\")\n .replace(/^www\\./i, \"\")\n .split(\"/\")[0]\n .split(\":\")[0];\n }\n })();\n\n if (!rawHost || rawHost === \"localhost\") {\n return undefined;\n }\n\n return rawHost.startsWith(\".\") ? rawHost : `.${rawHost}`;\n }\n}\n\nexport const authService = new AuthService();\n\nexport type { ClientInfo, GeoLocation };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/auth/services/auth.service.ts"],"sourcesContent":["import { cookies } from \"next/headers\";\n\nimport { api, type RequestConfig } from \"../../../infra/api/client\";\nimport { ApiError } from \"../../../infra/api/types\";\nimport { whitelabelService } from \"../../whitelabel/services/whitelabel.service\";\nimport { buildWlOverrideFromJwt } from \"../utils/build-wl-override\";\nimport {\n ClientInfo,\n ForgotPasswordRequest,\n ForgotPasswordResponse,\n GeoLocation,\n LoginApiResponse,\n LoginRequest,\n LoginResponse,\n LogoutApiResponse,\n LogoutRequest,\n LogoutResponse,\n RegisterApiRequest,\n RegisterApiResponse,\n RegisterRequest,\n RegisterResponse,\n ResendVerificationRequest,\n ResendVerificationResponse,\n ResetPasswordRequest,\n ResetPasswordResponse,\n SearchPlansApiResponse,\n SessionKeepRequest,\n SessionKeepResponse,\n SocialGoogleApiResponse,\n SocialGoogleResponse,\n SocialOnboardingApiResponse,\n SocialOnboardingRequest,\n SocialOnboardingResponse,\n IsolatedLoginResponse,\n TwoFactorApiResponse,\n TwoFactorRequest,\n TwoFactorResponse,\n UnlinkGoogleApiResponse,\n UnlinkGoogleResponse,\n VerifyEmailRequest,\n VerifyEmailResponse,\n} from \"../schema\";\nimport { findWhitelabel } from \"../../../server\";\nimport { resolveLocale } from \"../../../i18n/resolve-locale\";\nimport { localeToApiLanguage } from \"../../../utils/intl/locales\";\n\nconst AUTH_COOKIE_NAME = \"greatapps\";\nconst COOKIE_MAX_AGE = 60 * 60 * 24 * 30;\n\n/** Plano Free (ID fixo). */\nconst FREE_PLAN_ID = 5;\n\n/**\n * O cadastro pede o Free de duas formas, e as duas valem: ausência de plano (que o\n * `resolvePlanId` traduz para o sentinela -1) e o id do próprio Free.\n *\n * Olhar só o sentinela fazia `?id_plan=5` cair no ramo `trial` e criar um trial de 7 dias\n * do plano gratuito, com `date_due` e `trial_days` — estado que não existe no produto.\n */\nfunction isFreePlan(idPlan: number): boolean {\n return idPlan === -1 || idPlan === FREE_PLAN_ID;\n}\n\nconst COOKIE_OPTIONS = {\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\" as const,\n path: \"/\",\n};\n\nclass AuthService {\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n ): Promise<LoginResponse>;\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n options: { setCookie: false },\n ): Promise<IsolatedLoginResponse>;\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n options?: { setCookie?: boolean },\n ): Promise<LoginResponse | IsolatedLoginResponse>;\n async login(\n credentials: LoginRequest,\n clientInfo: ClientInfo,\n options?: { setCookie?: boolean },\n ): Promise<LoginResponse | IsolatedLoginResponse> {\n let requestConfig: RequestConfig | undefined;\n if (credentials.id_wl) {\n const wlToken = await whitelabelService.getTokenByWhitelabelId(\n credentials.id_wl,\n );\n requestConfig = { whiteLabelId: credentials.id_wl, authToken: wlToken };\n }\n\n const response = await api.apps.post<LoginApiResponse>(\n \"/auth/login\",\n {\n email: credentials.email,\n password: credentials.password,\n source: credentials?.source,\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n },\n requestConfig,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"E-mail ou senha incorretos\",\n response.code || \"LOGIN_FAILED\",\n 401,\n );\n }\n\n if (!response.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n if (response.two_factor_required) {\n return { result: \"two_factor_required\", cookie: response.cookie, twoFactorMode: response.two_factor_required };\n }\n\n // Modo isolado: caller só quer o token, sem afetar a sessão do navegador\n // nem pagar pelas chamadas de user/account (ex.: fluxo de SSO).\n if (options?.setCookie === false) {\n return { result: \"success\", accessToken: response.cookie };\n }\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(\n requestConfig ? { authToken: requestConfig.authToken } : undefined,\n ),\n accountService.findCurrentAccount(requestConfig),\n ]);\n\n return {\n result: \"success\",\n user,\n account,\n accessToken: response.cookie,\n refreshToken: \"\",\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n };\n }\n\n async verifyTwoFactor(\n cookie: string,\n code: string,\n clientInfo: ClientInfo,\n options?: { window?: number; setCookie?: boolean },\n ): Promise<TwoFactorResponse> {\n const payload: TwoFactorRequest = {\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n cookie,\n code,\n };\n\n const endpoint = options?.window !== undefined ? `/auth/code?window=${options.window}` : '/auth/code';\n const response = await api.apps.post<TwoFactorApiResponse>(\n endpoint,\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\"Código 2FA inválido\", \"TWO_FACTOR_FAILED\", 401);\n }\n\n if (!response?.data?.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida após 2FA\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n // Modo isolado: devolve o token sem gravar o cookie — o caller redireciona\n // pra outro lugar (ex.: SSO callback) e não quer mexer na sessão atual.\n if (options?.setCookie === false) {\n return { status: 1, accessToken: response.data.cookie };\n }\n\n await this.setAuthCookie(response.data.cookie);\n\n return { status: 1 };\n }\n\n async socialLoginGoogle(\n code: string,\n clientInfo: ClientInfo,\n ): Promise<SocialGoogleResponse> {\n const response = await api.apps.post<SocialGoogleApiResponse>(\n \"/auth/social/google\",\n {\n code,\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n verification: true,\n },\n );\n\n if (response.status === 0) {\n if (response.code === \"link_requires_password\") {\n return {\n result: \"link_requires_password\",\n message:\n response.message ||\n \"Uma conta com esse e-mail já existe. Faça login com e-mail e senha para vincular sua conta Google.\",\n };\n }\n throw new ApiError(\n response.message || \"Falha no login com Google\",\n response.code || \"GOOGLE_LOGIN_FAILED\",\n 401,\n );\n }\n\n if (response.needs_onboarding) {\n if (!response.onboarding_token || !response.partial?.email) {\n throw new ApiError(\n \"Resposta de onboarding inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n return {\n result: \"needs_onboarding\",\n onboardingToken: response.onboarding_token,\n partial: response.partial,\n };\n }\n\n if (!response.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n if (response.two_factor_required) {\n return {\n result: \"two_factor_required\",\n cookie: response.cookie,\n twoFactorMode: response.two_factor_required,\n };\n }\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(),\n accountService.findCurrentAccount(),\n ]);\n\n return {\n result: \"success\",\n user,\n account,\n accessToken: response.cookie,\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n };\n }\n\n async completeGoogleOnboarding(\n data: SocialOnboardingRequest,\n clientInfo: ClientInfo,\n ): Promise<SocialOnboardingResponse> {\n const today = new Date();\n const trialEndDate = new Date(today);\n const trialDays = Math.min(Math.max(data.trialDays ?? 7, 1), 90);\n trialEndDate.setDate(today.getDate() + trialDays);\n\n const idPlan = await this.resolvePlanId(data.idPlan);\n\n const whitelabel = await findWhitelabel().catch(() => null);\n const language = localeToApiLanguage(await resolveLocale({ whitelabelId: whitelabel?.id ?? null }));\n\n const payload = {\n onboarding_token: data.onboardingToken,\n name: data.user.name,\n bussiness_type: data.businessType ?? 0,\n language,\n timezone: clientInfo.timezone,\n currency: \"BRL\",\n location: clientInfo.location,\n ip: clientInfo.ip,\n agent: clientInfo.agent,\n id_affiliate: data.affiliateId || 0,\n origin: data.origin,\n user: {\n id_api: \"\",\n name: data.user.name || \"\",\n last_name: data.user.last_name || \"\",\n rg: data.user.rg || \"\",\n cpf: data.user.cpf || \"\",\n gender: data.user.gender ?? 1,\n ddi: data.user.ddi || \"55\",\n phone: data.user.phone || \"\",\n profile: \"owner\",\n language,\n },\n subscription:\n isFreePlan(idPlan)\n ? {\n type: \"free\",\n id_coupon: data.couponId || 0,\n id_plan: FREE_PLAN_ID,\n id_product: 1,\n }\n : {\n type: \"trial\",\n id_coupon: data.couponId || 0,\n id_plan: idPlan,\n date_due: trialEndDate.toISOString().split(\"T\")[0],\n trial_days: trialDays,\n id_product: 1,\n },\n };\n\n const response = await api.apps.post<SocialOnboardingApiResponse>(\n \"/auth/social/onboarding\",\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao finalizar cadastro com Google\",\n \"ONBOARDING_FAILED\",\n 400,\n );\n }\n\n if (!response.data?.length || !response.cookie) {\n throw new ApiError(\n \"Resposta de cadastro inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(),\n accountService.findCurrentAccount(),\n ]);\n\n return {\n user,\n account,\n accessToken: response.cookie,\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n };\n }\n\n async unlinkGoogle(): Promise<UnlinkGoogleResponse> {\n const { id_account, id_user } = await import(\n \"../utils/get-user-context\"\n ).then((m) => m.getUserContext());\n\n const response = await api.apps.delete<UnlinkGoogleApiResponse>(\n `/accounts/${id_account}/users/${id_user}/social/google`,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao desvincular conta Google\",\n \"UNLINK_FAILED\",\n 400,\n );\n }\n\n return {\n success: true,\n message: response.message,\n };\n }\n\n async register(\n data: RegisterRequest,\n clientInfo: ClientInfo,\n ): Promise<RegisterResponse> {\n const today = new Date();\n const trialEndDate = new Date(today);\n const trialDays = Math.min(Math.max(data.trialDays ?? 7, 1), 90);\n trialEndDate.setDate(today.getDate() + trialDays);\n\n const idPlan = await this.resolvePlanId(data.idPlan);\n\n const whitelabel = await findWhitelabel().catch(() => null);\n const language = localeToApiLanguage(await resolveLocale({ whitelabelId: whitelabel?.id ?? null }));\n\n const payload: RegisterApiRequest = {\n name: data.accountName,\n bussiness_type: data.businessType ?? 0,\n language,\n timezone: clientInfo.timezone,\n currency: \"BRL\",\n location: clientInfo.location,\n ip: clientInfo.ip,\n agent: clientInfo.agent,\n id_affiliate: data.affiliateId || 0,\n origin: data.origin,\n user: {\n id_api: \"\",\n name: data.name,\n last_name: data.lastName || \"\",\n rg: data.rg || \"\",\n cpf: data.cpf || \"\",\n gender: data.gender,\n email: data.email,\n phone: data.phone,\n password: data.password,\n profile: \"owner\",\n language,\n },\n subscription:\n isFreePlan(idPlan)\n ? {\n type: \"free\",\n id_coupon: data.couponId || 0,\n id_plan: FREE_PLAN_ID,\n id_product: 1,\n }\n : {\n type: \"trial\",\n id_coupon: data.couponId || 0,\n id_plan: idPlan,\n // Fluxo não-Stripe lê `date_due`; fluxo Stripe lê `trial_days`.\n // Mandamos os dois pra cobrir ambos os caminhos da API.\n date_due: trialEndDate.toISOString().split(\"T\")[0],\n trial_days: trialDays,\n id_product: 1,\n },\n };\n\n const response = await api.apps.post<RegisterApiResponse>(\n \"/accounts\",\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao criar conta\",\n \"REGISTER_FAILED\",\n 400,\n );\n }\n\n if (!response.data || response.data.length === 0) {\n throw new ApiError(\n \"Resposta de registro inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n if (!response.cookie) {\n throw new ApiError(\n \"Resposta de autenticação inválida\",\n \"INVALID_RESPONSE\",\n 500,\n );\n }\n\n const accountData = response.data[0];\n\n await this.setAuthCookie(response.cookie);\n\n const [{ userService }, { accountService }] = await Promise.all([\n import(\"../../users/services/user.service\"),\n import(\"../../accounts/services/account.service\"),\n ]);\n const [user, account] = await Promise.all([\n userService.findById(),\n accountService.findCurrentAccount(),\n ]);\n\n return {\n user,\n account,\n accessToken: response.cookie,\n refreshToken: \"\",\n expiresAt: new Date(Date.now() + COOKIE_MAX_AGE * 1000).toISOString(),\n requiresEmailVerification: !accountData.verified,\n };\n }\n\n async logout(clientInfo: ClientInfo): Promise<LogoutResponse> {\n const cookie = await this.getToken();\n\n if (!cookie) {\n throw new ApiError(\n \"Usuário não autenticado\",\n \"NOT_AUTHENTICATED_LOGOUT\",\n 401\n );\n }\n\n const payload: LogoutRequest = {\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n cookie,\n };\n\n try {\n const response = await api.apps.post<LogoutApiResponse>(\n \"/auth/logout\",\n payload,\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || \"Erro ao realizar logout\",\n response.code || \"LOGOUT_FAILED\",\n 400,\n );\n }\n } finally {\n await this.removeAuthCookie();\n }\n\n return {\n success: true,\n };\n }\n\n async forgotPassword(\n _data: ForgotPasswordRequest,\n ): Promise<ForgotPasswordResponse> {\n throw new ApiError(\n \"Recuperação de senha não implementada\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async resetPassword(\n _data: ResetPasswordRequest,\n ): Promise<ResetPasswordResponse> {\n throw new ApiError(\n \"Redefinição de senha não implementada\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async verifyEmail(_data: VerifyEmailRequest): Promise<VerifyEmailResponse> {\n throw new ApiError(\n \"Verificação de email não implementada\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async resendVerification(\n _data: ResendVerificationRequest,\n ): Promise<ResendVerificationResponse> {\n throw new ApiError(\n \"Reenvio de verificação não implementado\",\n \"NOT_IMPLEMENTED\",\n 501,\n );\n }\n\n async validateSession(clientInfo: ClientInfo): Promise<boolean> {\n const cookie = await this.getToken();\n\n if (!cookie) {\n return false;\n }\n\n try {\n const requestConfig = await buildWlOverrideFromJwt(cookie);\n\n const payload: SessionKeepRequest = {\n location: clientInfo.location,\n ip: clientInfo.ip,\n timezone: clientInfo.timezone,\n agent: clientInfo.agent,\n cookie,\n };\n\n const response = await api.apps.post<SessionKeepResponse>(\n \"/auth/keep\",\n payload,\n requestConfig,\n );\n\n return response.status === 1;\n } catch (error) {\n console.error(\"[AuthService] validateSession error\", error);\n return false;\n }\n }\n\n async isAuthenticated(): Promise<boolean> {\n const token = await this.getToken();\n return !!token;\n }\n\n async getToken(): Promise<string | undefined> {\n if (process.env.DUMMY_AUTH_TOKEN) return process.env.DUMMY_AUTH_TOKEN;\n const cookieStore = await cookies();\n return cookieStore.get(AUTH_COOKIE_NAME)?.value;\n }\n\n private async resolvePlanId(idPlan?: number | string | null): Promise<number> {\n if (idPlan == null || idPlan === \"\") return -1;\n if (typeof idPlan === \"number\" || Number(idPlan)) return Number(idPlan);\n\n if (typeof idPlan === \"string\") {\n const response = await api.apps.get<SearchPlansApiResponse>(\n `/plans?search=${encodeURIComponent(idPlan)}`,\n );\n\n if (response.status === 1 && response.data?.length) {\n return response.data[0].id;\n }\n\n throw new ApiError(\n `Plano \"${idPlan}\" não encontrado`,\n \"PLAN_NOT_FOUND\",\n 404,\n );\n }\n return -1;\n }\n\n private async setAuthCookie(token: string): Promise<void> {\n const cookieStore = await cookies();\n const whitelabel = await findWhitelabel().catch(() => null);\n const cookieDomain =\n this.normalizeCookieDomain(whitelabel?.domain);\n\n cookieStore.set(AUTH_COOKIE_NAME, token, {\n ...COOKIE_OPTIONS,\n ...(cookieDomain ? { domain: cookieDomain } : {}),\n maxAge: COOKIE_MAX_AGE,\n });\n }\n\n async removeAuthCookie(): Promise<void> {\n const cookieStore = await cookies();\n const whitelabel = await findWhitelabel().catch(() => null);\n const cookieDomain =\n this.normalizeCookieDomain(whitelabel?.domain);\n\n cookieStore.delete({\n name: AUTH_COOKIE_NAME,\n ...COOKIE_OPTIONS,\n ...(cookieDomain ? { domain: cookieDomain } : {}),\n });\n }\n\n private normalizeCookieDomain(domain?: string | null): string | undefined {\n if (!domain) return undefined;\n\n const normalized = domain.trim();\n if (!normalized) return undefined;\n\n const rawHost = (() => {\n try {\n return new URL(normalized).hostname;\n } catch {\n return normalized\n .replace(/^https?:\\/\\//i, \"\")\n .replace(/^www\\./i, \"\")\n .split(\"/\")[0]\n .split(\":\")[0];\n }\n })();\n\n if (!rawHost || rawHost === \"localhost\") {\n return undefined;\n }\n\n return rawHost.startsWith(\".\") ? rawHost : `.${rawHost}`;\n }\n}\n\nexport const authService = new AuthService();\n\nexport type { ClientInfo, GeoLocation };\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const DOM_MUTATION_GUARD_SCRIPT = `(function(){
|
|
2
|
+
if(typeof Node==="undefined"||!Node.prototype)return;
|
|
3
|
+
if(window.__gpDomGuard)return;
|
|
4
|
+
var s={removeChild:0,insertBefore:0};
|
|
5
|
+
window.__gpDomGuard=s;
|
|
6
|
+
window.__gpDomGuardOnHit=window.__gpDomGuardOnHit||null;
|
|
7
|
+
function hit(k){s[k]++;try{if(window.__gpDomGuardOnHit)window.__gpDomGuardOnHit(k)}catch(e){}}
|
|
8
|
+
var removeChild=Node.prototype.removeChild;
|
|
9
|
+
Node.prototype.removeChild=function(child){
|
|
10
|
+
if(child&&child.parentNode!==this){hit("removeChild");return child}
|
|
11
|
+
return removeChild.apply(this,arguments)};
|
|
12
|
+
var insertBefore=Node.prototype.insertBefore;
|
|
13
|
+
Node.prototype.insertBefore=function(newNode,referenceNode){
|
|
14
|
+
if(referenceNode&&referenceNode.parentNode!==this){hit("insertBefore");return newNode}
|
|
15
|
+
return insertBefore.apply(this,arguments)};
|
|
16
|
+
})();`;
|
|
17
|
+
export {
|
|
18
|
+
DOM_MUTATION_GUARD_SCRIPT
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=dom-mutation-guard.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/dom/dom-mutation-guard.ts"],"sourcesContent":["/**\n * Guarda contra mutação externa do DOM (tradutor do navegador, extensões).\n *\n * PROBLEMA: o Google Tradutor do Chrome substitui cada text node por `<font><font>…</font></font>`.\n * O React continua guardando a referência do text node ORIGINAL no fiber. No commit seguinte ele\n * chama `parent.insertBefore(novo, textNodeAntigo)` ou `parent.removeChild(textNodeAntigo)` — e o nó\n * já não é mais filho daquele pai. O DOM levanta `NotFoundError`, o erro sobe até o root e o app\n * inteiro cai no `global-error.tsx` (evento `render.root_crashed` no Sentry).\n *\n * Isso derrubou clientes reais em CO/PE/MX/AR/FR (zero casos no BR, onde a página já está no idioma\n * do usuário e o Chrome não oferece tradução), inclusive em cima da tela de trocar cartão — o cliente\n * ficava sem conseguir pagar.\n *\n * SOLUÇÃO: transformar essas duas operações em no-op quando o nó de referência já não pertence mais\n * ao pai. O React segue adiante, a página não morre, e o trecho traduzido apenas deixa de receber\n * aquela atualização pontual (o próximo re-render completo normaliza).\n *\n * Precisa rodar ANTES da hidratação, por isso é injetado como `<script>` inline no `<head>` do root\n * layout em vez de virar um `useEffect`.\n */\n\ndeclare global {\n interface Window {\n /** Contadores de quantas vezes a guarda entrou em ação nesta sessão. */\n __gpDomGuard?: { removeChild: number; insertBefore: number };\n /** Hook opcional preenchido pelo reporter para avisar o Sentry no primeiro disparo. */\n __gpDomGuardOnHit?: ((kind: 'removeChild' | 'insertBefore') => void) | null;\n }\n}\n\nexport const DOM_MUTATION_GUARD_SCRIPT = `(function(){\nif(typeof Node===\"undefined\"||!Node.prototype)return;\nif(window.__gpDomGuard)return;\nvar s={removeChild:0,insertBefore:0};\nwindow.__gpDomGuard=s;\nwindow.__gpDomGuardOnHit=window.__gpDomGuardOnHit||null;\nfunction hit(k){s[k]++;try{if(window.__gpDomGuardOnHit)window.__gpDomGuardOnHit(k)}catch(e){}}\nvar removeChild=Node.prototype.removeChild;\nNode.prototype.removeChild=function(child){\nif(child&&child.parentNode!==this){hit(\"removeChild\");return child}\nreturn removeChild.apply(this,arguments)};\nvar insertBefore=Node.prototype.insertBefore;\nNode.prototype.insertBefore=function(newNode,referenceNode){\nif(referenceNode&&referenceNode.parentNode!==this){hit(\"insertBefore\");return newNode}\nreturn insertBefore.apply(this,arguments)};\n})();`;\n"],"mappings":"AA8BO,MAAM,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
|
|
@@ -6,8 +6,13 @@ const LANGUAGE_OPTIONS = [
|
|
|
6
6
|
function buildLocaleOptions() {
|
|
7
7
|
return LANGUAGE_OPTIONS;
|
|
8
8
|
}
|
|
9
|
+
function localeToApiLanguage(locale) {
|
|
10
|
+
const option = LANGUAGE_OPTIONS.find((o) => o.value === locale);
|
|
11
|
+
return option?.apiValue ?? locale;
|
|
12
|
+
}
|
|
9
13
|
export {
|
|
10
14
|
LANGUAGE_OPTIONS,
|
|
11
|
-
buildLocaleOptions
|
|
15
|
+
buildLocaleOptions,
|
|
16
|
+
localeToApiLanguage
|
|
12
17
|
};
|
|
13
18
|
//# sourceMappingURL=locales.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/intl/locales.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/intl/locales.ts"],"sourcesContent":["import { Locale } from '../../i18n/config';\r\n\r\nexport type LocaleOption = {\r\n value: string;\r\n label: string;\r\n displayValue: string;\r\n apiValue: string;\r\n};\r\n\r\nexport const LANGUAGE_OPTIONS: LocaleOption[] = [\r\n { value: 'pt-br', label: 'Português (Brasil)', displayValue: 'Português (Brasil)', apiValue: 'pt-br' },\r\n { value: 'en-us', label: 'Inglês (EUA)', displayValue: 'Inglês (EUA)', apiValue: 'en' },\r\n { value: 'es-es', label: 'Espanhol', displayValue: 'Espanhol', apiValue: 'es' },\r\n];\r\n\r\nexport function buildLocaleOptions(): LocaleOption[] {\r\n return LANGUAGE_OPTIONS;\r\n}\r\n\r\nexport function localeToApiLanguage(locale: Locale): string {\r\n const option = LANGUAGE_OPTIONS.find((o) => o.value === locale);\r\n return option?.apiValue ?? locale;\r\n}\r\n"],"mappings":"AASO,MAAM,mBAAmC;AAAA,EAC9C,EAAE,OAAO,SAAS,OAAO,yBAAsB,cAAc,yBAAsB,UAAU,QAAQ;AAAA,EACrG,EAAE,OAAO,SAAS,OAAO,mBAAgB,cAAc,mBAAgB,UAAU,KAAK;AAAA,EACtF,EAAE,OAAO,SAAS,OAAO,YAAY,cAAc,YAAY,UAAU,KAAK;AAChF;AAEO,SAAS,qBAAqC;AACnD,SAAO;AACT;AAEO,SAAS,oBAAoB,QAAwB;AAC1D,QAAM,SAAS,iBAAiB,KAAK,CAAC,MAAM,EAAE,UAAU,MAAM;AAC9D,SAAO,QAAQ,YAAY;AAC7B;","names":[]}
|
package/package.json
CHANGED
|
@@ -31,7 +31,6 @@ export default function DisableTwoFactorAuthModal({
|
|
|
31
31
|
useDisableTwoFactorAuthForm({ onClose, onSuccess });
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
|
-
// Abre por cima do modal de configurações da conta, que é irmão na árvore.
|
|
35
34
|
<DialogLayer>
|
|
36
35
|
<Dialog open={open} onOpenChange={handleClose}>
|
|
37
36
|
<DialogContent className="flex flex-col p-0 gap-0 max-w-full sm:max-w-full border-0 rounded-t-2xl rounded-b-none h-dvh top-0 bottom-0 left-0 right-0 translate-x-0 translate-y-0 lg:max-w-117.5 lg:h-auto lg:rounded-lg lg:border lg:top-[50%] lg:left-[50%] lg:right-auto lg:bottom-auto lg:translate-x-[-50%] lg:translate-y-[-50%]">
|
|
@@ -42,7 +42,6 @@ export default function TwoFactorAuthModal({ open, onClose, onSuccess }: TwoFact
|
|
|
42
42
|
|
|
43
43
|
if (step === 'recovery') {
|
|
44
44
|
return (
|
|
45
|
-
// Abre por cima do modal de configurações da conta, que é irmão na árvore.
|
|
46
45
|
<DialogLayer>
|
|
47
46
|
<Dialog open={open} onOpenChange={handleClose}>
|
|
48
47
|
<DialogContent
|
|
@@ -111,7 +110,6 @@ export default function TwoFactorAuthModal({ open, onClose, onSuccess }: TwoFact
|
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
return (
|
|
114
|
-
// Abre por cima do modal de configurações da conta, que é irmão na árvore.
|
|
115
113
|
<DialogLayer>
|
|
116
114
|
<Dialog open={open} onOpenChange={handleClose}>
|
|
117
115
|
<DialogContent
|
|
@@ -7,8 +7,9 @@ import { useRouter } from 'next/navigation';
|
|
|
7
7
|
import { toast } from 'sonner';
|
|
8
8
|
import { IconLock } from '@tabler/icons-react';
|
|
9
9
|
import { cn } from '../../../infra/utils/clsx';
|
|
10
|
-
import type
|
|
10
|
+
import { isLocale, type Locale } from '../../../i18n/config';
|
|
11
11
|
import { setLocaleCookie } from '../../../utils/intl/locale-cookie';
|
|
12
|
+
import { localeToApiLanguage } from '../../../utils/intl/locales';
|
|
12
13
|
import { Button } from '../../ui/buttons/Button';
|
|
13
14
|
import { Separator } from '../../ui/data-display/Separator';
|
|
14
15
|
import { Toast } from '../../ui/feedback/Toast';
|
|
@@ -112,7 +113,7 @@ export function PreferencesSection({ onClose }: PreferencesSectionProps) {
|
|
|
112
113
|
const doSave = async (data: PreferencesFormValues, accountFields: UpdateAccountRequest) => {
|
|
113
114
|
setIsSaving(true);
|
|
114
115
|
try {
|
|
115
|
-
const apiLanguage =
|
|
116
|
+
const apiLanguage = isLocale(data.language) ? localeToApiLanguage(data.language) : data.language.toLowerCase();
|
|
116
117
|
await updateAccountUser.mutateAsync({
|
|
117
118
|
language: apiLanguage,
|
|
118
119
|
receive_sms: data.receive_sms,
|
|
@@ -14,10 +14,6 @@ export function ModalManager({ registry }: ModalManagerProps) {
|
|
|
14
14
|
|
|
15
15
|
if (modalStack.length === 0) return null;
|
|
16
16
|
|
|
17
|
-
// Modais da pilha nascem irmaos na arvore React: sem isso, todos cairiam na
|
|
18
|
-
// mesma profundidade e na mesma faixa de z-index (ver layers.tsx). A
|
|
19
|
-
// posicao na pilha vira a profundidade, relativa a onde o ModalManager
|
|
20
|
-
// esta montado.
|
|
21
17
|
return (
|
|
22
18
|
<>
|
|
23
19
|
{modalStack.map((modal, index) => {
|
|
@@ -34,7 +34,6 @@ export function ImageTooSmallModal({
|
|
|
34
34
|
const dimensionText = `${minWidth}x${minHeight}px`
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
|
-
// Abre por cima do modal/uploader que a disparou.
|
|
38
37
|
<DialogLayer>
|
|
39
38
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
40
39
|
<DialogContent className="w-full md:w-[480px]! lg:w-[480px]! max-w-[480px]! max-[480px]:w-[calc(100%-32px)] p-0 gap-0">
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
export type DomMutationGuardHit = {
|
|
6
|
+
kind: 'removeChild' | 'insertBefore';
|
|
7
|
+
language: string;
|
|
8
|
+
languages: string;
|
|
9
|
+
documentLang: string;
|
|
10
|
+
translatedNodes: number;
|
|
11
|
+
path: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reporta quando a guarda de mutação de DOM (`DOM_MUTATION_GUARD_SCRIPT`) entra em ação.
|
|
16
|
+
*
|
|
17
|
+
* Sem isso a correção fica cega: o app para de cair, mas ninguém descobre quantos clientes estão
|
|
18
|
+
* navegando com o tradutor do navegador ligado. Reporta UMA vez por sessão — o objetivo é medir
|
|
19
|
+
* quantas sessões são afetadas, não quantas operações de DOM foram salvas.
|
|
20
|
+
*/
|
|
21
|
+
export function useDomMutationGuardHit(onHit: (hit: DomMutationGuardHit) => void): void {
|
|
22
|
+
const onHitRef = useRef(onHit);
|
|
23
|
+
onHitRef.current = onHit;
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let reported = false;
|
|
27
|
+
|
|
28
|
+
const report = (kind: 'removeChild' | 'insertBefore') => {
|
|
29
|
+
if (reported) return;
|
|
30
|
+
reported = true;
|
|
31
|
+
window.__gpDomGuardOnHit = null;
|
|
32
|
+
onHitRef.current({
|
|
33
|
+
kind,
|
|
34
|
+
language: navigator.language,
|
|
35
|
+
languages: navigator.languages?.join(',') ?? '',
|
|
36
|
+
documentLang: document.documentElement.lang,
|
|
37
|
+
// <font> sem classe é a assinatura do Google Tradutor
|
|
38
|
+
translatedNodes: document.querySelectorAll('font[style]').length,
|
|
39
|
+
path: window.location.pathname,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Disparos que aconteceram antes deste componente montar.
|
|
44
|
+
const already = window.__gpDomGuard;
|
|
45
|
+
if (already && (already.removeChild > 0 || already.insertBefore > 0)) {
|
|
46
|
+
report(already.insertBefore > 0 ? 'insertBefore' : 'removeChild');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
window.__gpDomGuardOnHit = report;
|
|
51
|
+
return () => {
|
|
52
|
+
window.__gpDomGuardOnHit = null;
|
|
53
|
+
};
|
|
54
|
+
}, []);
|
|
55
|
+
}
|
|
@@ -39,14 +39,24 @@ export interface ResolveLocaleOptions {
|
|
|
39
39
|
userLanguage?: string | null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function localeFromAcceptLanguage(header: string | null): Locale | null {
|
|
43
|
+
if (!header) return null;
|
|
44
|
+
for (const entry of header.split(',')) {
|
|
45
|
+
const locale = normalizeLocale(entry.split(';')[0].trim());
|
|
46
|
+
if (locale) return locale;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
/**
|
|
43
52
|
* Resolve o locale da requisição (server-side). Ordem de prioridade:
|
|
44
53
|
*
|
|
45
54
|
* 1. **Whitelabel não-padrão** (`id_wl != 1`) → `pt` (por enquanto, whitelabels não são localizadas).
|
|
46
55
|
* 2. **Preferência da conta** (`userLanguage`), quando fornecida.
|
|
47
56
|
* 3. **Cookie `NEXT_LOCALE`** — cache da preferência ou escolha manual do visitante.
|
|
48
|
-
* 4. **
|
|
49
|
-
* 5. **
|
|
57
|
+
* 4. **Header `Accept-Language`** do navegador.
|
|
58
|
+
* 5. **Inferência por país** — JWT (`session.location.country`) ou header `CF-IPCountry`.
|
|
59
|
+
* 6. **Fallback** → {@link DEFAULT_LOCALE}.
|
|
50
60
|
*/
|
|
51
61
|
export async function resolveLocale(options: ResolveLocaleOptions = {}): Promise<Locale> {
|
|
52
62
|
const cookieStore = await cookies();
|
|
@@ -67,12 +77,17 @@ export async function resolveLocale(options: ResolveLocaleOptions = {}): Promise
|
|
|
67
77
|
const fromCookie = normalizeLocale(cookieStore.get(LOCALE_COOKIE)?.value);
|
|
68
78
|
if (fromCookie) return fromCookie;
|
|
69
79
|
|
|
70
|
-
// 4. Inferência por país (geo)
|
|
71
80
|
const headersList = await headers();
|
|
81
|
+
|
|
82
|
+
// 4. Header Accept-Language do navegador
|
|
83
|
+
const fromAcceptLanguage = localeFromAcceptLanguage(headersList.get('accept-language'));
|
|
84
|
+
if (fromAcceptLanguage) return fromAcceptLanguage;
|
|
85
|
+
|
|
86
|
+
// 5. Inferência por país (geo)
|
|
72
87
|
const country = jwt?.country ?? headersList.get(GEO_HEADER);
|
|
73
88
|
const fromGeo = localeFromCountry(country);
|
|
74
89
|
if (fromGeo) return fromGeo;
|
|
75
90
|
|
|
76
|
-
//
|
|
91
|
+
// 6. Fallback
|
|
77
92
|
return DEFAULT_LOCALE;
|
|
78
93
|
}
|
package/src/index.ts
CHANGED
|
@@ -515,6 +515,9 @@ export { default as usePasswordVisibility } from "./hooks/usePasswordVisibility"
|
|
|
515
515
|
export { default as useCountdownTimer } from "./hooks/useCountdownTimer";
|
|
516
516
|
export { default as useIsMobile } from "./hooks/useIsMobile";
|
|
517
517
|
export { useDebounce } from "./hooks/useDebounce";
|
|
518
|
+
export { useDomMutationGuardHit } from "./hooks/useDomMutationGuardHit";
|
|
519
|
+
export type { DomMutationGuardHit } from "./hooks/useDomMutationGuardHit";
|
|
520
|
+
export { DOM_MUTATION_GUARD_SCRIPT } from "./utils/dom/dom-mutation-guard";
|
|
518
521
|
export { useDebouncedEffect } from "./hooks/useDebouncedEffect";
|
|
519
522
|
export { useDebounceState } from "./hooks/useDebounceState";
|
|
520
523
|
|
package/src/infra/api/client.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
import { headers as nextHeaders } from "next/headers";
|
|
2
|
+
|
|
1
3
|
import { ApiError } from "./types";
|
|
2
4
|
import { findWhitelabel } from "../../modules/whitelabel/actions/find-whitelabel.action";
|
|
3
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Headers de tracing distribuído do Sentry, repassados da requisição que chegou neste worker
|
|
8
|
+
* para a chamada de saída à r3-api.
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ POR QUE PRECISA SER EXPLÍCITO. O browser já gera o trace e já manda `sentry-trace`/`baggage`
|
|
11
|
+
* para o worker Next (same-origin, o browser SDK anexa sozinho). O que quebra é o SEGUNDO hop:
|
|
12
|
+
* este `ApiClient` montava `{ authorization, Content-Type }` do zero e descartava o contexto, e
|
|
13
|
+
* `@sentry/nextjs` rodando em workerd não instrumenta fetch de saída (a auto-instrumentação de
|
|
14
|
+
* `undici` é do runtime Node e não tem onde se enganchar aqui). Sem estas duas linhas, um incidente
|
|
15
|
+
* não se segue do browser até o backend: são dois traces sem ligação nenhuma.
|
|
16
|
+
*
|
|
17
|
+
* Do lado da r3-api os campos entram no log estruturado como `trace_id`/`parent_span_id`
|
|
18
|
+
* (ver `docs/logs-estruturados.md` lá), então o MESMO id aparece no Sentry do front e no Workers
|
|
19
|
+
* Logs do backend — uma busca resolve os dois.
|
|
20
|
+
*/
|
|
21
|
+
const TRACE_HEADERS = ["sentry-trace", "baggage"] as const;
|
|
22
|
+
|
|
4
23
|
export interface ApiClientOptions {
|
|
5
24
|
/**
|
|
6
25
|
* Quando true, monta `{base}/{locale}/{wlId}{endpoint}` (ex.: r2-clone).
|
|
@@ -73,6 +92,28 @@ class ApiClient {
|
|
|
73
92
|
headers["Content-Type"] = "application/json";
|
|
74
93
|
}
|
|
75
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Repassa o tracing quando existe. Sem mecanismo novo: `request()` já aguarda
|
|
97
|
+
* `findWhitelabel()`, que já é um `'use server'` lendo `next/headers` — é a mesma chamada, no
|
|
98
|
+
* mesmo ponto do fluxo. Fica aqui dentro de propósito: os dois call sites de
|
|
99
|
+
* `getDefaultHeaders` passam a propagar sem precisar lembrar disso em cada um.
|
|
100
|
+
*
|
|
101
|
+
* Silencioso por design: fora de um contexto de requisição (build, script, job) `headers()`
|
|
102
|
+
* lança, e uma chamada de API não pode falhar por causa de observabilidade. Sem os headers, a
|
|
103
|
+
* r3-api simplesmente gera o próprio `request_id` e a linha continua correlacionável — só não
|
|
104
|
+
* se liga ao front.
|
|
105
|
+
*/
|
|
106
|
+
try {
|
|
107
|
+
const entrada = await nextHeaders();
|
|
108
|
+
|
|
109
|
+
for (const nome of TRACE_HEADERS) {
|
|
110
|
+
const valor = entrada.get(nome);
|
|
111
|
+
if (valor) headers[nome] = valor;
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
/* sem contexto de requisição — segue sem tracing */
|
|
115
|
+
}
|
|
116
|
+
|
|
76
117
|
return headers;
|
|
77
118
|
}
|
|
78
119
|
|
|
@@ -41,6 +41,8 @@ import {
|
|
|
41
41
|
VerifyEmailResponse,
|
|
42
42
|
} from "../schema";
|
|
43
43
|
import { findWhitelabel } from "../../../server";
|
|
44
|
+
import { resolveLocale } from "../../../i18n/resolve-locale";
|
|
45
|
+
import { localeToApiLanguage } from "../../../utils/intl/locales";
|
|
44
46
|
|
|
45
47
|
const AUTH_COOKIE_NAME = "greatapps";
|
|
46
48
|
const COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
|
@@ -295,12 +297,15 @@ class AuthService {
|
|
|
295
297
|
|
|
296
298
|
const idPlan = await this.resolvePlanId(data.idPlan);
|
|
297
299
|
|
|
300
|
+
const whitelabel = await findWhitelabel().catch(() => null);
|
|
301
|
+
const language = localeToApiLanguage(await resolveLocale({ whitelabelId: whitelabel?.id ?? null }));
|
|
302
|
+
|
|
298
303
|
const payload = {
|
|
299
304
|
onboarding_token: data.onboardingToken,
|
|
300
305
|
name: data.user.name,
|
|
301
306
|
bussiness_type: data.businessType ?? 0,
|
|
302
|
-
language
|
|
303
|
-
timezone:
|
|
307
|
+
language,
|
|
308
|
+
timezone: clientInfo.timezone,
|
|
304
309
|
currency: "BRL",
|
|
305
310
|
location: clientInfo.location,
|
|
306
311
|
ip: clientInfo.ip,
|
|
@@ -317,7 +322,7 @@ class AuthService {
|
|
|
317
322
|
ddi: data.user.ddi || "55",
|
|
318
323
|
phone: data.user.phone || "",
|
|
319
324
|
profile: "owner",
|
|
320
|
-
language
|
|
325
|
+
language,
|
|
321
326
|
},
|
|
322
327
|
subscription:
|
|
323
328
|
isFreePlan(idPlan)
|
|
@@ -411,11 +416,14 @@ class AuthService {
|
|
|
411
416
|
|
|
412
417
|
const idPlan = await this.resolvePlanId(data.idPlan);
|
|
413
418
|
|
|
419
|
+
const whitelabel = await findWhitelabel().catch(() => null);
|
|
420
|
+
const language = localeToApiLanguage(await resolveLocale({ whitelabelId: whitelabel?.id ?? null }));
|
|
421
|
+
|
|
414
422
|
const payload: RegisterApiRequest = {
|
|
415
423
|
name: data.accountName,
|
|
416
424
|
bussiness_type: data.businessType ?? 0,
|
|
417
|
-
language
|
|
418
|
-
timezone:
|
|
425
|
+
language,
|
|
426
|
+
timezone: clientInfo.timezone,
|
|
419
427
|
currency: "BRL",
|
|
420
428
|
location: clientInfo.location,
|
|
421
429
|
ip: clientInfo.ip,
|
|
@@ -433,7 +441,7 @@ class AuthService {
|
|
|
433
441
|
phone: data.phone,
|
|
434
442
|
password: data.password,
|
|
435
443
|
profile: "owner",
|
|
436
|
-
language
|
|
444
|
+
language,
|
|
437
445
|
},
|
|
438
446
|
subscription:
|
|
439
447
|
isFreePlan(idPlan)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guarda contra mutação externa do DOM (tradutor do navegador, extensões).
|
|
3
|
+
*
|
|
4
|
+
* PROBLEMA: o Google Tradutor do Chrome substitui cada text node por `<font><font>…</font></font>`.
|
|
5
|
+
* O React continua guardando a referência do text node ORIGINAL no fiber. No commit seguinte ele
|
|
6
|
+
* chama `parent.insertBefore(novo, textNodeAntigo)` ou `parent.removeChild(textNodeAntigo)` — e o nó
|
|
7
|
+
* já não é mais filho daquele pai. O DOM levanta `NotFoundError`, o erro sobe até o root e o app
|
|
8
|
+
* inteiro cai no `global-error.tsx` (evento `render.root_crashed` no Sentry).
|
|
9
|
+
*
|
|
10
|
+
* Isso derrubou clientes reais em CO/PE/MX/AR/FR (zero casos no BR, onde a página já está no idioma
|
|
11
|
+
* do usuário e o Chrome não oferece tradução), inclusive em cima da tela de trocar cartão — o cliente
|
|
12
|
+
* ficava sem conseguir pagar.
|
|
13
|
+
*
|
|
14
|
+
* SOLUÇÃO: transformar essas duas operações em no-op quando o nó de referência já não pertence mais
|
|
15
|
+
* ao pai. O React segue adiante, a página não morre, e o trecho traduzido apenas deixa de receber
|
|
16
|
+
* aquela atualização pontual (o próximo re-render completo normaliza).
|
|
17
|
+
*
|
|
18
|
+
* Precisa rodar ANTES da hidratação, por isso é injetado como `<script>` inline no `<head>` do root
|
|
19
|
+
* layout em vez de virar um `useEffect`.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface Window {
|
|
24
|
+
/** Contadores de quantas vezes a guarda entrou em ação nesta sessão. */
|
|
25
|
+
__gpDomGuard?: { removeChild: number; insertBefore: number };
|
|
26
|
+
/** Hook opcional preenchido pelo reporter para avisar o Sentry no primeiro disparo. */
|
|
27
|
+
__gpDomGuardOnHit?: ((kind: 'removeChild' | 'insertBefore') => void) | null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const DOM_MUTATION_GUARD_SCRIPT = `(function(){
|
|
32
|
+
if(typeof Node==="undefined"||!Node.prototype)return;
|
|
33
|
+
if(window.__gpDomGuard)return;
|
|
34
|
+
var s={removeChild:0,insertBefore:0};
|
|
35
|
+
window.__gpDomGuard=s;
|
|
36
|
+
window.__gpDomGuardOnHit=window.__gpDomGuardOnHit||null;
|
|
37
|
+
function hit(k){s[k]++;try{if(window.__gpDomGuardOnHit)window.__gpDomGuardOnHit(k)}catch(e){}}
|
|
38
|
+
var removeChild=Node.prototype.removeChild;
|
|
39
|
+
Node.prototype.removeChild=function(child){
|
|
40
|
+
if(child&&child.parentNode!==this){hit("removeChild");return child}
|
|
41
|
+
return removeChild.apply(this,arguments)};
|
|
42
|
+
var insertBefore=Node.prototype.insertBefore;
|
|
43
|
+
Node.prototype.insertBefore=function(newNode,referenceNode){
|
|
44
|
+
if(referenceNode&&referenceNode.parentNode!==this){hit("insertBefore");return newNode}
|
|
45
|
+
return insertBefore.apply(this,arguments)};
|
|
46
|
+
})();`;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Locale } from '../../i18n/config';
|
|
2
|
+
|
|
1
3
|
export type LocaleOption = {
|
|
2
4
|
value: string;
|
|
3
5
|
label: string;
|
|
@@ -14,3 +16,8 @@ export const LANGUAGE_OPTIONS: LocaleOption[] = [
|
|
|
14
16
|
export function buildLocaleOptions(): LocaleOption[] {
|
|
15
17
|
return LANGUAGE_OPTIONS;
|
|
16
18
|
}
|
|
19
|
+
|
|
20
|
+
export function localeToApiLanguage(locale: Locale): string {
|
|
21
|
+
const option = LANGUAGE_OPTIONS.find((o) => o.value === locale);
|
|
22
|
+
return option?.apiValue ?? locale;
|
|
23
|
+
}
|