@greatapps/common 1.1.803 → 1.1.805
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/infra/api/client.mjs +3 -3
- package/dist/infra/api/client.mjs.map +1 -1
- package/dist/modules/accounts/services/account.service.mjs +13 -8
- package/dist/modules/accounts/services/account.service.mjs.map +1 -1
- package/dist/modules/accounts/services/two-factor.service.mjs +3 -3
- package/dist/modules/accounts/services/two-factor.service.mjs.map +1 -1
- package/dist/modules/users/schema.mjs.map +1 -1
- package/dist/modules/users/services/user.service.mjs +4 -3
- package/dist/modules/users/services/user.service.mjs.map +1 -1
- package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs +2 -14
- package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
- package/dist/modules/whitelabel/actions/revalidate-whitelabel.action.mjs +1 -2
- package/dist/modules/whitelabel/actions/revalidate-whitelabel.action.mjs.map +1 -1
- package/dist/modules/whitelabel/utils/find-whitelabel-token.mjs +10 -0
- package/dist/modules/whitelabel/utils/find-whitelabel-token.mjs.map +1 -0
- package/dist/modules/whitelabel/utils/resolve-whitelabel-for-host.mjs +20 -0
- package/dist/modules/whitelabel/utils/resolve-whitelabel-for-host.mjs.map +1 -0
- package/dist/providers/query.provider.mjs +1 -1
- package/dist/providers/query.provider.mjs.map +1 -1
- package/dist/server.mjs +2 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/infra/api/client.ts +6 -6
- package/src/modules/accounts/services/account.service.ts +16 -8
- package/src/modules/accounts/services/two-factor.service.ts +3 -3
- package/src/modules/accounts/types.ts +1 -3
- package/src/modules/auth/schema.ts +0 -1
- package/src/modules/users/schema.ts +1 -1
- package/src/modules/users/services/user.service.ts +5 -3
- package/src/modules/whitelabel/actions/find-whitelabel.action.ts +2 -18
- package/src/modules/whitelabel/actions/revalidate-whitelabel.action.ts +1 -2
- package/src/modules/whitelabel/schema.ts +0 -1
- package/src/modules/whitelabel/utils/find-whitelabel-token.ts +14 -0
- package/src/modules/whitelabel/utils/resolve-whitelabel-for-host.ts +27 -0
- package/src/providers/query.provider.tsx +1 -1
- package/src/server.ts +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { headers as nextHeaders } from "next/headers";
|
|
2
2
|
import { ApiError } from "./types";
|
|
3
|
-
import {
|
|
3
|
+
import { findWhitelabelToken } from "../../modules/whitelabel/utils/find-whitelabel-token";
|
|
4
4
|
const TRACE_HEADERS = ["sentry-trace", "baggage"];
|
|
5
5
|
class ApiClient {
|
|
6
6
|
baseUrl;
|
|
@@ -63,7 +63,7 @@ class ApiClient {
|
|
|
63
63
|
if (this.emitLogs) {
|
|
64
64
|
console.log("[ApiClient] Building URL and headers...");
|
|
65
65
|
}
|
|
66
|
-
const { id, token } = await
|
|
66
|
+
const { id, token } = await findWhitelabelToken();
|
|
67
67
|
const [url, defaultHeaders] = await Promise.all([
|
|
68
68
|
this.buildUrl(endpoint, overrideWlId ?? id),
|
|
69
69
|
this.getDefaultHeaders(overrideToken ?? token, fetchConfig.body instanceof FormData)
|
|
@@ -189,7 +189,7 @@ class ApiClient {
|
|
|
189
189
|
*/
|
|
190
190
|
async stream(endpoint, config = {}) {
|
|
191
191
|
const { timeout: _timeout, responseType: _responseType, whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;
|
|
192
|
-
const resolvido = overrideWlId != null && overrideToken != null ? { id: overrideWlId, token: overrideToken } : await
|
|
192
|
+
const resolvido = overrideWlId != null && overrideToken != null ? { id: overrideWlId, token: overrideToken } : await findWhitelabelToken();
|
|
193
193
|
const [url, defaultHeaders] = await Promise.all([
|
|
194
194
|
this.buildUrl(endpoint, overrideWlId ?? resolvido.id),
|
|
195
195
|
this.getDefaultHeaders(overrideToken ?? resolvido.token)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/infra/api/client.ts"],"sourcesContent":["import { headers as nextHeaders } from \"next/headers\";\n\nimport { ApiError } from \"./types\";\nimport { findWhitelabel } from \"../../modules/whitelabel/actions/find-whitelabel.action\";\n\n/**\n * Headers de tracing distribuído do Sentry, repassados da requisição que chegou neste worker\n * para a chamada de saída à r3-api.\n *\n * ⚠️ POR QUE PRECISA SER EXPLÍCITO. O browser já gera o trace e já manda `sentry-trace`/`baggage`\n * para o worker Next (same-origin, o browser SDK anexa sozinho). O que quebra é o SEGUNDO hop:\n * este `ApiClient` montava `{ authorization, Content-Type }` do zero e descartava o contexto, e\n * `@sentry/nextjs` rodando em workerd não instrumenta fetch de saída (a auto-instrumentação de\n * `undici` é do runtime Node e não tem onde se enganchar aqui). Sem estas duas linhas, um incidente\n * não se segue do browser até o backend: são dois traces sem ligação nenhuma.\n *\n * Do lado da r3-api os campos entram no log estruturado como `trace_id`/`parent_span_id`\n * (ver `docs/logs-estruturados.md` lá), então o MESMO id aparece no Sentry do front e no Workers\n * Logs do backend — uma busca resolve os dois.\n */\nconst TRACE_HEADERS = [\"sentry-trace\", \"baggage\"] as const;\n\nexport interface ApiClientOptions {\n /**\n * Quando true, monta `{base}/{locale}/{wlId}{endpoint}` (ex.: r2-clone).\n * Padrão false: `{base}/v1/{locale}/{wlId}{endpoint}`.\n */\n omitApiVersion?: boolean;\n\n /** Habilita logs detalhados das requisições (útil para desenvolvimento) */\n emitLogs?: boolean;\n}\n\ninterface ApiErrorResponseBody {\n message?: string | { message?: string };\n code?: string;\n}\n\nexport interface RequestConfig extends RequestInit {\n timeout?: number;\n /** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */\n responseType?: \"json\" | \"text\";\n /** Força um whiteLabelId específico na URL, ignorando o findWhitelabel(). */\n whiteLabelId?: number;\n /** Força um token específico no header Authorization, ignorando o findWhitelabel(). */\n authToken?: string;\n}\n\nclass ApiClient {\n private readonly baseUrl: string;\n private readonly apiVersion = \"v1\";\n private readonly apiLocale = \"pt-br\";\n private readonly omitApiVersion: boolean;\n private readonly emitLogs: boolean = false;\n\n constructor(baseUrl: string, options?: ApiClientOptions) {\n this.baseUrl = baseUrl.replace(/\\/$/, \"\");\n this.omitApiVersion = options?.omitApiVersion ?? false;\n this.emitLogs = options?.emitLogs ?? false;\n }\n\n private async buildUrl(\n endpoint: string,\n whiteLabelId: number,\n ): Promise<string> {\n const wlPath = this.omitApiVersion\n ? `/${this.apiLocale}/${whiteLabelId}`\n : `/${this.apiVersion}/${this.apiLocale}/${whiteLabelId}`;\n const path = endpoint.startsWith(\"/\") ? endpoint : `/${endpoint}`;\n const url = `${this.baseUrl}${wlPath}${path}`;\n if (this.emitLogs) {\n console.log(\"[ApiClient] buildUrl\", {\n baseURL: this.baseUrl,\n whiteLabelId,\n endpoint,\n fullUrl: url,\n });\n }\n return url;\n }\n\n private async getDefaultHeaders(\n token: string,\n isFormData = false,\n ): Promise<HeadersInit> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] getDefaultHeaders\", {\n token: token.substring(0, 20) + \"...\",\n });\n }\n\n const headers: HeadersInit = {\n authorization: token,\n };\n\n if (!isFormData) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n /**\n * Repassa o tracing quando existe. Sem mecanismo novo: `request()` já aguarda\n * `findWhitelabel()`, que já é um `'use server'` lendo `next/headers` — é a mesma chamada, no\n * mesmo ponto do fluxo. Fica aqui dentro de propósito: os dois call sites de\n * `getDefaultHeaders` passam a propagar sem precisar lembrar disso em cada um.\n *\n * Silencioso por design: fora de um contexto de requisição (build, script, job) `headers()`\n * lança, e uma chamada de API não pode falhar por causa de observabilidade. Sem os headers, a\n * r3-api simplesmente gera o próprio `request_id` e a linha continua correlacionável — só não\n * se liga ao front.\n */\n try {\n const entrada = await nextHeaders();\n\n for (const nome of TRACE_HEADERS) {\n const valor = entrada.get(nome);\n if (valor) headers[nome] = valor;\n }\n } catch {\n /* sem contexto de requisição — segue sem tracing */\n }\n\n return headers;\n }\n\n async request<T>(endpoint: string, config: RequestConfig = {}): Promise<T> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] request called\", {\n endpoint,\n method: config.method,\n });\n }\n\n const { timeout = 30000, responseType = \"json\", whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n if (this.emitLogs) {\n console.log(\"[ApiClient] Building URL and headers...\");\n }\n\n const { id, token } = await findWhitelabel();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? id),\n this.getDefaultHeaders(overrideToken ?? token, fetchConfig.body instanceof FormData),\n ]);\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Fetching\", {\n url,\n method: fetchConfig.method,\n headers: defaultHeaders,\n });\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: {\n ...defaultHeaders,\n ...fetchConfig.headers,\n },\n signal: controller.signal,\n });\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Response received\", {\n status: response.status,\n ok: response.ok,\n });\n\n if (!response.ok) {\n const errBody = await response.text();\n let message = `HTTP error ${response.status} ${response.statusText} ${errBody}`;\n let code = \"HTTP_ERROR\";\n try {\n const errorData: ApiErrorResponseBody = JSON.parse(errBody);\n if (typeof errorData.message === \"string\") {\n message = errorData.message;\n } else if (errorData.message?.message) {\n message = errorData.message.message;\n }\n if (typeof errorData.code === \"string\") code = errorData.code;\n } catch {\n if (errBody.trim()) message = errBody.trim();\n }\n const method = fetchConfig.method ?? \"GET\";\n let bodyPreview: unknown;\n if (method !== \"GET\" && fetchConfig.body != null) {\n if (typeof fetchConfig.body === \"string\") {\n try {\n bodyPreview = JSON.parse(fetchConfig.body);\n } catch {\n bodyPreview = fetchConfig.body;\n }\n } else if (fetchConfig.body instanceof FormData) {\n bodyPreview = \"[FormData]\";\n } else {\n bodyPreview = fetchConfig.body;\n }\n }\n console.error(\"[ApiClient] Request failed\", {\n from: url,\n method,\n ...(bodyPreview !== undefined ? { body: bodyPreview } : {}),\n status: response.status,\n message,\n });\n throw new ApiError(message, code, response.status);\n }\n\n if (responseType === \"text\") {\n return (await response.text()) as T;\n }\n\n const data = (await response.json()) as T;\n if (this.emitLogs)\n console.log(\"[ApiClient] Request successful\", { hasData: !!data });\n return data;\n } catch (error) {\n if (this.emitLogs) console.error(\"[ApiClient] Request error\", { error });\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"Tempo de requisição excedido\", \"TIMEOUT\", 408);\n }\n throw new ApiError(error.message, \"NETWORK_ERROR\", 0);\n }\n\n throw new ApiError(\"Erro desconhecido\", \"UNKNOWN_ERROR\", 500);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async get<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"GET\" });\n }\n\n async post<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"POST\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async put<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PUT\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async patch<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PATCH\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async delete<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"DELETE\" });\n }\n\n /**\n * Requisição cuja resposta é consumida em stream: devolve a `Response` crua, sem ler o\n * corpo e sem timeout.\n *\n * `request()` não serve para isso por dois motivos. Ele faz `await response.json()`, o\n * que espera o corpo inteiro e mata o propósito do stream; e aborta em 30s, que é o\n * tempo que uma operação longa leva justamente por ser longa — gerar o link de uma\n * página com 132 imagens copia 100 MB e passa disso (ID-5112).\n *\n * O corpo é do chamador: quem recebe decide quando ler e quando cancelar.\n */\n async stream(endpoint: string, config: RequestConfig = {}): Promise<Response> {\n const { timeout: _timeout, responseType: _responseType, whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n // Só resolve o whitelabel quando falta algum dos dois: com ambos vindos do chamador, a\n // consulta seria uma ida à rede para descartar o resultado.\n const resolvido = overrideWlId != null && overrideToken != null\n ? { id: overrideWlId, token: overrideToken }\n : await findWhitelabel();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? resolvido.id),\n this.getDefaultHeaders(overrideToken ?? resolvido.token),\n ]);\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: { ...defaultHeaders, ...fetchConfig.headers },\n // Sem isto o fetch do Next segura o corpo inteiro antes de devolver, e o stream chega\n // completo de uma vez no fim: medido em 32s até o primeiro evento, contra 0,4s com\n // no-store. O progresso existiria no protocolo e não na tela.\n cache: \"no-store\",\n });\n\n if (!response.ok || !response.body) {\n const detalhe = await response.text().catch(() => \"\");\n throw new ApiError(\n detalhe.trim() || `HTTP error ${response.status} ${response.statusText}`,\n \"HTTP_ERROR\",\n response.status,\n );\n }\n\n return response;\n }\n}\n\nconst api = {\n apps: new ApiClient(\n process.env.GAPPS_R3_API_URL || \"https://r3-api.greatapps.dev.br\",\n ),\n pages: new ApiClient(\n process.env.GPAGES_R3_API_URL || \"https://r3-api.greatpages.dev.br\",\n ),\n};\n\n/** @deprecated use api.apps */\nexport const apiClient = api.apps;\n\nexport { api, ApiClient };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/infra/api/client.ts"],"sourcesContent":["import { headers as nextHeaders } from \"next/headers\";\n\nimport { ApiError } from \"./types\";\nimport { findWhitelabelToken } from \"../../modules/whitelabel/utils/find-whitelabel-token\";\n\n/**\n * Headers de tracing distribuído do Sentry, repassados da requisição que chegou neste worker\n * para a chamada de saída à r3-api.\n *\n * ⚠️ POR QUE PRECISA SER EXPLÍCITO. O browser já gera o trace e já manda `sentry-trace`/`baggage`\n * para o worker Next (same-origin, o browser SDK anexa sozinho). O que quebra é o SEGUNDO hop:\n * este `ApiClient` montava `{ authorization, Content-Type }` do zero e descartava o contexto, e\n * `@sentry/nextjs` rodando em workerd não instrumenta fetch de saída (a auto-instrumentação de\n * `undici` é do runtime Node e não tem onde se enganchar aqui). Sem estas duas linhas, um incidente\n * não se segue do browser até o backend: são dois traces sem ligação nenhuma.\n *\n * Do lado da r3-api os campos entram no log estruturado como `trace_id`/`parent_span_id`\n * (ver `docs/logs-estruturados.md` lá), então o MESMO id aparece no Sentry do front e no Workers\n * Logs do backend — uma busca resolve os dois.\n */\nconst TRACE_HEADERS = [\"sentry-trace\", \"baggage\"] as const;\n\nexport interface ApiClientOptions {\n /**\n * Quando true, monta `{base}/{locale}/{wlId}{endpoint}` (ex.: r2-clone).\n * Padrão false: `{base}/v1/{locale}/{wlId}{endpoint}`.\n */\n omitApiVersion?: boolean;\n\n /** Habilita logs detalhados das requisições (útil para desenvolvimento) */\n emitLogs?: boolean;\n}\n\ninterface ApiErrorResponseBody {\n message?: string | { message?: string };\n code?: string;\n}\n\nexport interface RequestConfig extends RequestInit {\n timeout?: number;\n /** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */\n responseType?: \"json\" | \"text\";\n /** Força um whiteLabelId específico na URL, ignorando o findWhitelabelToken(). */\n whiteLabelId?: number;\n /** Força um token específico no header Authorization, ignorando o findWhitelabelToken(). */\n authToken?: string;\n}\n\nclass ApiClient {\n private readonly baseUrl: string;\n private readonly apiVersion = \"v1\";\n private readonly apiLocale = \"pt-br\";\n private readonly omitApiVersion: boolean;\n private readonly emitLogs: boolean = false;\n\n constructor(baseUrl: string, options?: ApiClientOptions) {\n this.baseUrl = baseUrl.replace(/\\/$/, \"\");\n this.omitApiVersion = options?.omitApiVersion ?? false;\n this.emitLogs = options?.emitLogs ?? false;\n }\n\n private async buildUrl(\n endpoint: string,\n whiteLabelId: number,\n ): Promise<string> {\n const wlPath = this.omitApiVersion\n ? `/${this.apiLocale}/${whiteLabelId}`\n : `/${this.apiVersion}/${this.apiLocale}/${whiteLabelId}`;\n const path = endpoint.startsWith(\"/\") ? endpoint : `/${endpoint}`;\n const url = `${this.baseUrl}${wlPath}${path}`;\n if (this.emitLogs) {\n console.log(\"[ApiClient] buildUrl\", {\n baseURL: this.baseUrl,\n whiteLabelId,\n endpoint,\n fullUrl: url,\n });\n }\n return url;\n }\n\n private async getDefaultHeaders(\n token: string,\n isFormData = false,\n ): Promise<HeadersInit> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] getDefaultHeaders\", {\n token: token.substring(0, 20) + \"...\",\n });\n }\n\n const headers: HeadersInit = {\n authorization: token,\n };\n\n if (!isFormData) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n /**\n * Repassa o tracing quando existe. Sem mecanismo novo: `request()` já aguarda\n * `findWhitelabelToken()`, que já lê `next/headers` — é a mesma chamada, no\n * mesmo ponto do fluxo. Fica aqui dentro de propósito: os dois call sites de\n * `getDefaultHeaders` passam a propagar sem precisar lembrar disso em cada um.\n *\n * Silencioso por design: fora de um contexto de requisição (build, script, job) `headers()`\n * lança, e uma chamada de API não pode falhar por causa de observabilidade. Sem os headers, a\n * r3-api simplesmente gera o próprio `request_id` e a linha continua correlacionável — só não\n * se liga ao front.\n */\n try {\n const entrada = await nextHeaders();\n\n for (const nome of TRACE_HEADERS) {\n const valor = entrada.get(nome);\n if (valor) headers[nome] = valor;\n }\n } catch {\n /* sem contexto de requisição — segue sem tracing */\n }\n\n return headers;\n }\n\n async request<T>(endpoint: string, config: RequestConfig = {}): Promise<T> {\n if (this.emitLogs) {\n console.log(\"[ApiClient] request called\", {\n endpoint,\n method: config.method,\n });\n }\n\n const { timeout = 30000, responseType = \"json\", whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n if (this.emitLogs) {\n console.log(\"[ApiClient] Building URL and headers...\");\n }\n\n const { id, token } = await findWhitelabelToken();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? id),\n this.getDefaultHeaders(overrideToken ?? token, fetchConfig.body instanceof FormData),\n ]);\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Fetching\", {\n url,\n method: fetchConfig.method,\n headers: defaultHeaders,\n });\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: {\n ...defaultHeaders,\n ...fetchConfig.headers,\n },\n signal: controller.signal,\n });\n\n if (this.emitLogs)\n console.log(\"[ApiClient] Response received\", {\n status: response.status,\n ok: response.ok,\n });\n\n if (!response.ok) {\n const errBody = await response.text();\n let message = `HTTP error ${response.status} ${response.statusText} ${errBody}`;\n let code = \"HTTP_ERROR\";\n try {\n const errorData: ApiErrorResponseBody = JSON.parse(errBody);\n if (typeof errorData.message === \"string\") {\n message = errorData.message;\n } else if (errorData.message?.message) {\n message = errorData.message.message;\n }\n if (typeof errorData.code === \"string\") code = errorData.code;\n } catch {\n if (errBody.trim()) message = errBody.trim();\n }\n const method = fetchConfig.method ?? \"GET\";\n let bodyPreview: unknown;\n if (method !== \"GET\" && fetchConfig.body != null) {\n if (typeof fetchConfig.body === \"string\") {\n try {\n bodyPreview = JSON.parse(fetchConfig.body);\n } catch {\n bodyPreview = fetchConfig.body;\n }\n } else if (fetchConfig.body instanceof FormData) {\n bodyPreview = \"[FormData]\";\n } else {\n bodyPreview = fetchConfig.body;\n }\n }\n console.error(\"[ApiClient] Request failed\", {\n from: url,\n method,\n ...(bodyPreview !== undefined ? { body: bodyPreview } : {}),\n status: response.status,\n message,\n });\n throw new ApiError(message, code, response.status);\n }\n\n if (responseType === \"text\") {\n return (await response.text()) as T;\n }\n\n const data = (await response.json()) as T;\n if (this.emitLogs)\n console.log(\"[ApiClient] Request successful\", { hasData: !!data });\n return data;\n } catch (error) {\n if (this.emitLogs) console.error(\"[ApiClient] Request error\", { error });\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"Tempo de requisição excedido\", \"TIMEOUT\", 408);\n }\n throw new ApiError(error.message, \"NETWORK_ERROR\", 0);\n }\n\n throw new ApiError(\"Erro desconhecido\", \"UNKNOWN_ERROR\", 500);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async get<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"GET\" });\n }\n\n async post<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"POST\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async put<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PUT\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async patch<T>(\n endpoint: string,\n data?: unknown,\n config?: RequestConfig,\n ): Promise<T> {\n return this.request<T>(endpoint, {\n ...config,\n method: \"PATCH\",\n body:\n data instanceof FormData\n ? data\n : data\n ? JSON.stringify(data)\n : undefined,\n });\n }\n\n async delete<T>(endpoint: string, config?: RequestConfig): Promise<T> {\n return this.request<T>(endpoint, { ...config, method: \"DELETE\" });\n }\n\n /**\n * Requisição cuja resposta é consumida em stream: devolve a `Response` crua, sem ler o\n * corpo e sem timeout.\n *\n * `request()` não serve para isso por dois motivos. Ele faz `await response.json()`, o\n * que espera o corpo inteiro e mata o propósito do stream; e aborta em 30s, que é o\n * tempo que uma operação longa leva justamente por ser longa — gerar o link de uma\n * página com 132 imagens copia 100 MB e passa disso (ID-5112).\n *\n * O corpo é do chamador: quem recebe decide quando ler e quando cancelar.\n */\n async stream(endpoint: string, config: RequestConfig = {}): Promise<Response> {\n const { timeout: _timeout, responseType: _responseType, whiteLabelId: overrideWlId, authToken: overrideToken, ...fetchConfig } = config;\n\n // Só resolve o whitelabel quando falta algum dos dois: com ambos vindos do chamador, a\n // consulta seria uma ida à rede para descartar o resultado.\n const resolvido = overrideWlId != null && overrideToken != null\n ? { id: overrideWlId, token: overrideToken }\n : await findWhitelabelToken();\n\n const [url, defaultHeaders] = await Promise.all([\n this.buildUrl(endpoint, overrideWlId ?? resolvido.id),\n this.getDefaultHeaders(overrideToken ?? resolvido.token),\n ]);\n\n const response = await fetch(url, {\n ...fetchConfig,\n headers: { ...defaultHeaders, ...fetchConfig.headers },\n // Sem isto o fetch do Next segura o corpo inteiro antes de devolver, e o stream chega\n // completo de uma vez no fim: medido em 32s até o primeiro evento, contra 0,4s com\n // no-store. O progresso existiria no protocolo e não na tela.\n cache: \"no-store\",\n });\n\n if (!response.ok || !response.body) {\n const detalhe = await response.text().catch(() => \"\");\n throw new ApiError(\n detalhe.trim() || `HTTP error ${response.status} ${response.statusText}`,\n \"HTTP_ERROR\",\n response.status,\n );\n }\n\n return response;\n }\n}\n\nconst api = {\n apps: new ApiClient(\n process.env.GAPPS_R3_API_URL || \"https://r3-api.greatapps.dev.br\",\n ),\n pages: new ApiClient(\n process.env.GPAGES_R3_API_URL || \"https://r3-api.greatpages.dev.br\",\n ),\n};\n\n/** @deprecated use api.apps */\nexport const apiClient = api.apps;\n\nexport { api, ApiClient };\n"],"mappings":"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","names":[]}
|
|
@@ -18,11 +18,16 @@ class AccountService {
|
|
|
18
18
|
400
|
|
19
19
|
);
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
const { user, ...account } = response.data[0];
|
|
22
|
+
return account;
|
|
23
|
+
}
|
|
24
|
+
pickUsersQueryParams(params = {}) {
|
|
25
|
+
const { page, limit, search, name, notInProject, profiles, firstId } = params;
|
|
26
|
+
return { page, limit, search, name, notInProject, profiles, firstId };
|
|
22
27
|
}
|
|
23
28
|
async listAccountUsers(params) {
|
|
24
29
|
const { id_account } = await getUserContext();
|
|
25
|
-
const queryParams = buildQueryParams(params);
|
|
30
|
+
const queryParams = buildQueryParams(this.pickUsersQueryParams(params));
|
|
26
31
|
const endpoint = `/accounts/${id_account}/users?${queryParams}`;
|
|
27
32
|
const response = await api.apps.get(endpoint);
|
|
28
33
|
if (response.status === 0) {
|
|
@@ -43,7 +48,7 @@ class AccountService {
|
|
|
43
48
|
*/
|
|
44
49
|
async listAccountMembers(params) {
|
|
45
50
|
const { id_account, id_user } = await getUserContext();
|
|
46
|
-
const queryParams = buildQueryParams({ ...params, scopeUserId: id_user });
|
|
51
|
+
const queryParams = buildQueryParams({ ...this.pickUsersQueryParams(params), scopeUserId: id_user });
|
|
47
52
|
const response = await api.apps.get(
|
|
48
53
|
`/accounts/${id_account}/users?${queryParams}`
|
|
49
54
|
);
|
|
@@ -73,7 +78,7 @@ class AccountService {
|
|
|
73
78
|
400
|
|
74
79
|
);
|
|
75
80
|
}
|
|
76
|
-
return response;
|
|
81
|
+
return { status: response.status, message: response.message };
|
|
77
82
|
}
|
|
78
83
|
async deleteAccountUser(userId) {
|
|
79
84
|
const { id_account } = await getUserContext();
|
|
@@ -83,7 +88,7 @@ class AccountService {
|
|
|
83
88
|
if (response.status === 0) {
|
|
84
89
|
throw new ApiError(response.message || "Erro ao deletar usu\xE1rio", "DELETE_USER_FAILED", 400);
|
|
85
90
|
}
|
|
86
|
-
return response;
|
|
91
|
+
return { status: response.status, message: response.message };
|
|
87
92
|
}
|
|
88
93
|
async deleteAccount(data) {
|
|
89
94
|
const { id_account } = await getUserContext();
|
|
@@ -108,7 +113,7 @@ class AccountService {
|
|
|
108
113
|
if (response.status === 0) {
|
|
109
114
|
throw new ApiError(response.message || "Erro ao criar usu\xE1rio", "CREATE_USER_FAILED", 400);
|
|
110
115
|
}
|
|
111
|
-
return response;
|
|
116
|
+
return { status: response.status, message: response.message };
|
|
112
117
|
}
|
|
113
118
|
async resolvePhotoField(user) {
|
|
114
119
|
if (user.photo === void 0) return {};
|
|
@@ -128,7 +133,7 @@ class AccountService {
|
|
|
128
133
|
400
|
|
129
134
|
);
|
|
130
135
|
}
|
|
131
|
-
return response;
|
|
136
|
+
return { status: response.status, message: response.message };
|
|
132
137
|
}
|
|
133
138
|
async updateAccountUserById(userId, user) {
|
|
134
139
|
const { id_account } = await getUserContext();
|
|
@@ -143,7 +148,7 @@ class AccountService {
|
|
|
143
148
|
400
|
|
144
149
|
);
|
|
145
150
|
}
|
|
146
|
-
return response;
|
|
151
|
+
return { status: response.status, message: response.message };
|
|
147
152
|
}
|
|
148
153
|
async updateAccount(data) {
|
|
149
154
|
const { id_account } = await getUserContext();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/accounts/services/account.service.ts"],"sourcesContent":["import 'server-only';\n\nimport { api } from '../../../infra/api/client';\nimport { ApiError } from '../../../infra/api/types';\nimport { buildQueryParams } from '../../../infra/utils/params';\nimport { assertAccountNotFrozen } from '../../auth/utils/assert-account-not-frozen';\nimport { getUserContext } from '../../auth/utils/get-user-context';\nimport { fileToBase64 } from '../../../utils/file/index';\nimport type { RequestConfig } from '../../../infra/api/client';\nimport { buildWlOverride } from '../../auth/utils/build-wl-override';\nimport type {\n Account,\n AccountUserProjectsResponse,\n AccountUsersPaginationParams,\n ChangePasswordRequest,\n ChangePasswordResponse,\n ChangeUserPhotoResponse,\n CreateAccountUserRequest,\n CreateAccountUserResponse,\n DeleteAccountRequest,\n DeleteAccountResponse,\n DeleteAccountUserResponse,\n GetAccountDataResponse,\n ListAccountUsersResponse,\n UpdateAccountRequest,\n UpdateAccountResponse,\n UpdateAccountUserRequest,\n UpdateAccountUserResponse,\n} from '../types';\n\nclass AccountService {\n async findCurrentAccount(config?: RequestConfig): Promise<Account> {\n const { id_wl, id_account } = await getUserContext();\n const effectiveConfig = config ?? await buildWlOverride(id_wl);\n const response = await api.apps.get<GetAccountDataResponse>(`/accounts/${id_account}`, effectiveConfig);\n\n if (response.status === 0 || !response.data?.[0]) {\n throw new ApiError(\n response.message || 'Erro ao buscar dados da conta',\n 'GET_ACCOUNT_FAILED',\n 400\n );\n }\n\n return response.data[0];\n }\n\n async listAccountUsers(params?: Partial<AccountUsersPaginationParams>) {\n const { id_account } = await getUserContext();\n const queryParams = buildQueryParams(params);\n const endpoint = `/accounts/${id_account}/users?${queryParams}`;\n const response = await api.apps.get<ListAccountUsersResponse>(endpoint);\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao listar usuários',\n 'LIST_ACCOUNT_USERS_FAILED',\n 400\n );\n }\n\n return {\n users: response.data || [],\n total: response.total || 0,\n };\n }\n\n /**\n * Usuários da conta com projetos (`total_projects`, `projects`), escopados ao usuário logado:\n * collaborator/viewer só veem quem divide projeto com eles.\n */\n async listAccountMembers(params?: Partial<AccountUsersPaginationParams>) {\n const { id_account, id_user } = await getUserContext();\n const queryParams = buildQueryParams({ ...params, scopeUserId: id_user });\n const response = await api.apps.get<ListAccountUsersResponse>(\n `/accounts/${id_account}/users?${queryParams}`\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao listar usuários',\n 'LIST_ACCOUNT_USERS_FAILED',\n 400\n );\n }\n\n return {\n users: response.data || [],\n total: response.total || 0,\n };\n }\n\n async changeUserPhoto(userId: number, photo: File): Promise<ChangeUserPhotoResponse> {\n const { id_account } = await getUserContext();\n const base64 = await fileToBase64(photo);\n const response = await api.apps.put<ChangeUserPhotoResponse>(\n `/accounts/${id_account}/users/${userId}/file/photo`,\n { file: base64 }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao mudar foto do usuário',\n 'CHANGE_USER_PHOTO_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async deleteAccountUser(userId: number): Promise<DeleteAccountUserResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.delete<DeleteAccountUserResponse>(\n `/accounts/${id_account}/users/${userId}`\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao deletar usuário', 'DELETE_USER_FAILED', 400);\n }\n\n return response;\n }\n\n async deleteAccount(data: DeleteAccountRequest): Promise<DeleteAccountResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.delete<DeleteAccountResponse>(\n `/accounts/${id_account}`,\n { body: JSON.stringify(data) }\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao deletar conta', 'DELETE_ACCOUNT_FAILED', 400);\n }\n\n return response;\n }\n\n async createAccountUser(user: CreateAccountUserRequest): Promise<CreateAccountUserResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.post<CreateAccountUserResponse>(\n `/accounts/${id_account}/users`,\n {\n ...user,\n photo: user.photo ? await fileToBase64(user.photo) : null,\n }\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao criar usuário', 'CREATE_USER_FAILED', 400);\n }\n\n return response;\n }\n\n private async resolvePhotoField(user: UpdateAccountUserRequest): Promise<{ photo?: string | null }> {\n if (user.photo === undefined) return {};\n const photo =\n user.photo != null && typeof user.photo !== 'string'\n ? await fileToBase64(user.photo)\n : user.photo;\n return { photo };\n }\n\n async updateAccountUser(user: UpdateAccountUserRequest): Promise<UpdateAccountUserResponse> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.put<UpdateAccountUserResponse>(\n `/accounts/${id_account}/users/${id_user}`,\n { ...user, ...(await this.resolvePhotoField(user)) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao atualizar usuário',\n 'UPDATE_USER_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async updateAccountUserById(\n userId: number,\n user: UpdateAccountUserRequest\n ): Promise<UpdateAccountUserResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.put<UpdateAccountUserResponse>(\n `/accounts/${id_account}/users/${userId}`,\n { ...user, ...(await this.resolvePhotoField(user)) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao atualizar usuário',\n 'UPDATE_USER_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async updateAccount(data: UpdateAccountRequest): Promise<UpdateAccountResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.put<UpdateAccountResponse>(`/accounts/${id_account}`, data);\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao atualizar conta',\n 'UPDATE_ACCOUNT_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async requestContactReset(type: 'email' | 'phone', newContact: string, ddi?: string): Promise<void> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.post<{ status: number; message: string }>(\n `/accounts/${id_account}/users/${id_user}/resetContact/request`,\n { type, newContact, ...(ddi ? { ddi } : {}) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao solicitar alteração',\n 'REQUEST_CONTACT_RESET_FAILED',\n 400\n );\n }\n }\n\n async confirmContactReset(type: 'email' | 'phone', token: string, newContact: string, ddi?: string): Promise<void> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.post<{ status: number; message: string }>(\n `/accounts/${id_account}/users/${id_user}/resetContact/confirm`,\n { type, token, newContact, ...(ddi ? { ddi } : {}) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Código inválido ou expirado',\n 'CONFIRM_CONTACT_RESET_FAILED',\n 400\n );\n }\n }\n\n /** Checagem de e-mail já cadastrado, usada no cadastro de usuário antes de avançar de passo. */\n async checkUserExists(email: string): Promise<{ exists: boolean }> {\n const response = await api.apps.post<{ exist: boolean }>('/users/action/exists', { email });\n\n return { exists: response.exist };\n }\n\n async addUserProjects(userId: number, projectIds: number[]): Promise<AccountUserProjectsResponse> {\n await assertAccountNotFrozen();\n const { id_account } = await getUserContext();\n const response = await api.apps.post<AccountUserProjectsResponse>(\n `/accounts/${id_account}/users/${userId}/action/addProjectsBatch`,\n { projects: projectIds }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao vincular projetos ao usuário',\n 'ADD_USER_PROJECTS_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async removeUserProjects(userId: number, projectIds: number[]): Promise<AccountUserProjectsResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.post<AccountUserProjectsResponse>(\n `/accounts/${id_account}/users/${userId}/action/removeProjectsBatch`,\n { projects: projectIds }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao desvincular projetos do usuário',\n 'REMOVE_USER_PROJECTS_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async changePassword(data: ChangePasswordRequest): Promise<ChangePasswordResponse> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.put<ChangePasswordResponse>(\n `/accounts/${id_account}/users/${id_user}/action/changePassword`,\n data\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao alterar senha',\n 'CHANGE_PASSWORD_FAILED',\n 400\n );\n }\n\n return response;\n }\n}\n\nexport const accountService = new AccountService();\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/accounts/services/account.service.ts"],"sourcesContent":["import 'server-only';\n\nimport { api } from '../../../infra/api/client';\nimport { ApiError } from '../../../infra/api/types';\nimport { buildQueryParams } from '../../../infra/utils/params';\nimport { assertAccountNotFrozen } from '../../auth/utils/assert-account-not-frozen';\nimport { getUserContext } from '../../auth/utils/get-user-context';\nimport { fileToBase64 } from '../../../utils/file/index';\nimport type { RequestConfig } from '../../../infra/api/client';\nimport { buildWlOverride } from '../../auth/utils/build-wl-override';\nimport type {\n Account,\n AccountUserProjectsResponse,\n AccountUsersPaginationParams,\n ChangePasswordRequest,\n ChangePasswordResponse,\n ChangeUserPhotoResponse,\n CreateAccountUserRequest,\n CreateAccountUserResponse,\n DeleteAccountRequest,\n DeleteAccountResponse,\n DeleteAccountUserResponse,\n GetAccountDataResponse,\n ListAccountUsersResponse,\n UpdateAccountRequest,\n UpdateAccountResponse,\n UpdateAccountUserRequest,\n UpdateAccountUserResponse,\n} from '../types';\n\nclass AccountService {\n async findCurrentAccount(config?: RequestConfig): Promise<Account> {\n const { id_wl, id_account } = await getUserContext();\n const effectiveConfig = config ?? await buildWlOverride(id_wl);\n const response = await api.apps.get<GetAccountDataResponse>(`/accounts/${id_account}`, effectiveConfig);\n\n if (response.status === 0 || !response.data?.[0]) {\n throw new ApiError(\n response.message || 'Erro ao buscar dados da conta',\n 'GET_ACCOUNT_FAILED',\n 400\n );\n }\n\n const { user, ...account } = response.data[0];\n\n return account;\n }\n\n private pickUsersQueryParams(params: Partial<AccountUsersPaginationParams> = {}) {\n const { page, limit, search, name, notInProject, profiles, firstId } = params;\n\n return { page, limit, search, name, notInProject, profiles, firstId };\n }\n\n async listAccountUsers(params?: Partial<AccountUsersPaginationParams>) {\n const { id_account } = await getUserContext();\n const queryParams = buildQueryParams(this.pickUsersQueryParams(params));\n const endpoint = `/accounts/${id_account}/users?${queryParams}`;\n const response = await api.apps.get<ListAccountUsersResponse>(endpoint);\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao listar usuários',\n 'LIST_ACCOUNT_USERS_FAILED',\n 400\n );\n }\n\n return {\n users: response.data || [],\n total: response.total || 0,\n };\n }\n\n /**\n * Usuários da conta com projetos (`total_projects`, `projects`), escopados ao usuário logado:\n * collaborator/viewer só veem quem divide projeto com eles.\n */\n async listAccountMembers(params?: Partial<AccountUsersPaginationParams>) {\n const { id_account, id_user } = await getUserContext();\n const queryParams = buildQueryParams({ ...this.pickUsersQueryParams(params), scopeUserId: id_user });\n const response = await api.apps.get<ListAccountUsersResponse>(\n `/accounts/${id_account}/users?${queryParams}`\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao listar usuários',\n 'LIST_ACCOUNT_USERS_FAILED',\n 400\n );\n }\n\n return {\n users: response.data || [],\n total: response.total || 0,\n };\n }\n\n async changeUserPhoto(userId: number, photo: File): Promise<ChangeUserPhotoResponse> {\n const { id_account } = await getUserContext();\n const base64 = await fileToBase64(photo);\n const response = await api.apps.put<ChangeUserPhotoResponse>(\n `/accounts/${id_account}/users/${userId}/file/photo`,\n { file: base64 }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao mudar foto do usuário',\n 'CHANGE_USER_PHOTO_FAILED',\n 400\n );\n }\n\n return { status: response.status, message: response.message };\n }\n\n async deleteAccountUser(userId: number): Promise<DeleteAccountUserResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.delete<DeleteAccountUserResponse>(\n `/accounts/${id_account}/users/${userId}`\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao deletar usuário', 'DELETE_USER_FAILED', 400);\n }\n\n return { status: response.status, message: response.message };\n }\n\n async deleteAccount(data: DeleteAccountRequest): Promise<DeleteAccountResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.delete<DeleteAccountResponse>(\n `/accounts/${id_account}`,\n { body: JSON.stringify(data) }\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao deletar conta', 'DELETE_ACCOUNT_FAILED', 400);\n }\n\n return response;\n }\n\n async createAccountUser(user: CreateAccountUserRequest): Promise<CreateAccountUserResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.post<CreateAccountUserResponse>(\n `/accounts/${id_account}/users`,\n {\n ...user,\n photo: user.photo ? await fileToBase64(user.photo) : null,\n }\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao criar usuário', 'CREATE_USER_FAILED', 400);\n }\n\n return { status: response.status, message: response.message };\n }\n\n private async resolvePhotoField(user: UpdateAccountUserRequest): Promise<{ photo?: string | null }> {\n if (user.photo === undefined) return {};\n const photo =\n user.photo != null && typeof user.photo !== 'string'\n ? await fileToBase64(user.photo)\n : user.photo;\n return { photo };\n }\n\n async updateAccountUser(user: UpdateAccountUserRequest): Promise<UpdateAccountUserResponse> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.put<UpdateAccountUserResponse>(\n `/accounts/${id_account}/users/${id_user}`,\n { ...user, ...(await this.resolvePhotoField(user)) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao atualizar usuário',\n 'UPDATE_USER_FAILED',\n 400\n );\n }\n\n return { status: response.status, message: response.message };\n }\n\n async updateAccountUserById(\n userId: number,\n user: UpdateAccountUserRequest\n ): Promise<UpdateAccountUserResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.put<UpdateAccountUserResponse>(\n `/accounts/${id_account}/users/${userId}`,\n { ...user, ...(await this.resolvePhotoField(user)) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao atualizar usuário',\n 'UPDATE_USER_FAILED',\n 400\n );\n }\n\n return { status: response.status, message: response.message };\n }\n\n async updateAccount(data: UpdateAccountRequest): Promise<UpdateAccountResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.put<UpdateAccountResponse>(`/accounts/${id_account}`, data);\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao atualizar conta',\n 'UPDATE_ACCOUNT_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async requestContactReset(type: 'email' | 'phone', newContact: string, ddi?: string): Promise<void> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.post<{ status: number; message: string }>(\n `/accounts/${id_account}/users/${id_user}/resetContact/request`,\n { type, newContact, ...(ddi ? { ddi } : {}) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao solicitar alteração',\n 'REQUEST_CONTACT_RESET_FAILED',\n 400\n );\n }\n }\n\n async confirmContactReset(type: 'email' | 'phone', token: string, newContact: string, ddi?: string): Promise<void> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.post<{ status: number; message: string }>(\n `/accounts/${id_account}/users/${id_user}/resetContact/confirm`,\n { type, token, newContact, ...(ddi ? { ddi } : {}) }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Código inválido ou expirado',\n 'CONFIRM_CONTACT_RESET_FAILED',\n 400\n );\n }\n }\n\n /** Checagem de e-mail já cadastrado, usada no cadastro de usuário antes de avançar de passo. */\n async checkUserExists(email: string): Promise<{ exists: boolean }> {\n const response = await api.apps.post<{ exist: boolean }>('/users/action/exists', { email });\n\n return { exists: response.exist };\n }\n\n async addUserProjects(userId: number, projectIds: number[]): Promise<AccountUserProjectsResponse> {\n await assertAccountNotFrozen();\n const { id_account } = await getUserContext();\n const response = await api.apps.post<AccountUserProjectsResponse>(\n `/accounts/${id_account}/users/${userId}/action/addProjectsBatch`,\n { projects: projectIds }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao vincular projetos ao usuário',\n 'ADD_USER_PROJECTS_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async removeUserProjects(userId: number, projectIds: number[]): Promise<AccountUserProjectsResponse> {\n const { id_account } = await getUserContext();\n const response = await api.apps.post<AccountUserProjectsResponse>(\n `/accounts/${id_account}/users/${userId}/action/removeProjectsBatch`,\n { projects: projectIds }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao desvincular projetos do usuário',\n 'REMOVE_USER_PROJECTS_FAILED',\n 400\n );\n }\n\n return response;\n }\n\n async changePassword(data: ChangePasswordRequest): Promise<ChangePasswordResponse> {\n const { id_account, id_user } = await getUserContext();\n const response = await api.apps.put<ChangePasswordResponse>(\n `/accounts/${id_account}/users/${id_user}/action/changePassword`,\n data\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao alterar senha',\n 'CHANGE_PASSWORD_FAILED',\n 400\n );\n }\n\n return response;\n }\n}\n\nexport const accountService = new AccountService();\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import { ApiError } from "../../../infra/api/types";
|
|
3
3
|
import { getUserContext } from "../../auth/utils/get-user-context";
|
|
4
|
-
import {
|
|
4
|
+
import { findWhitelabelToken } from "../../whitelabel/utils/find-whitelabel-token";
|
|
5
5
|
const TWO_FA_BASE_URL = process.env.R2_2FA_URL || "https://r2-2fa.greatapps.dev.br";
|
|
6
6
|
class TwoFactorService {
|
|
7
7
|
async request(path, body) {
|
|
8
|
-
const { id, token } = await
|
|
8
|
+
const { id, token } = await findWhitelabelToken();
|
|
9
9
|
const { id_account, id_user } = await getUserContext();
|
|
10
10
|
const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa/${path}`;
|
|
11
11
|
const response = await fetch(url, {
|
|
@@ -49,7 +49,7 @@ class TwoFactorService {
|
|
|
49
49
|
return response;
|
|
50
50
|
}
|
|
51
51
|
async disable() {
|
|
52
|
-
const { id, token } = await
|
|
52
|
+
const { id, token } = await findWhitelabelToken();
|
|
53
53
|
const { id_account, id_user } = await getUserContext();
|
|
54
54
|
const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa`;
|
|
55
55
|
const response = await fetch(url, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/accounts/services/two-factor.service.ts"],"sourcesContent":["import 'server-only';\r\n\r\nimport { ApiError } from '../../../infra/api/types';\r\nimport { getUserContext } from '../../auth/utils/get-user-context';\r\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/accounts/services/two-factor.service.ts"],"sourcesContent":["import 'server-only';\r\n\r\nimport { ApiError } from '../../../infra/api/types';\r\nimport { getUserContext } from '../../auth/utils/get-user-context';\r\nimport { findWhitelabelToken } from '../../whitelabel/utils/find-whitelabel-token';\r\nimport type { TwoFactorActionResponse, TwoFactorGenerateResponse } from '../types';\r\n\r\nconst TWO_FA_BASE_URL = process.env.R2_2FA_URL || 'https://r2-2fa.greatapps.dev.br';\r\n\r\nclass TwoFactorService {\r\n private async request<T>(path: string, body?: object): Promise<T> {\r\n const { id, token } = await findWhitelabelToken();\r\n const { id_account, id_user } = await getUserContext();\r\n\r\n const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa/${path}`;\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n authorization: token,\r\n },\r\n body: body ? JSON.stringify(body) : undefined,\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({})) as Record<string, unknown>;\r\n throw new ApiError(\r\n (errorData.message as string) || `HTTP error ${response.status}`,\r\n 'HTTP_ERROR',\r\n response.status\r\n );\r\n }\r\n\r\n return response.json() as Promise<T>;\r\n }\r\n\r\n async generate(): Promise<TwoFactorGenerateResponse> {\r\n const response = await this.request<TwoFactorGenerateResponse>('generate');\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(response.message || 'Erro ao gerar QR Code', 'GENERATE_2FA_FAILED', 400);\r\n }\r\n\r\n return response;\r\n }\r\n\r\n async confirm(code: string, options?: { window?: number }): Promise<TwoFactorActionResponse> {\r\n const path = options?.window !== undefined ? `confirm?window=${options.window}` : 'confirm';\r\n const response = await this.request<TwoFactorActionResponse>(path, { code });\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(response.message || 'Código inválido', 'CONFIRM_2FA_FAILED', 400);\r\n }\r\n\r\n return response;\r\n }\r\n\r\n async verify(code: string): Promise<TwoFactorActionResponse> {\r\n const response = await this.request<TwoFactorActionResponse>('verify', { code });\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(response.message || 'Código inválido', 'VERIFY_2FA_FAILED', 400);\r\n }\r\n\r\n return response;\r\n }\r\n\r\n async disable(): Promise<TwoFactorActionResponse> {\r\n const { id, token } = await findWhitelabelToken();\r\n const { id_account, id_user } = await getUserContext();\r\n\r\n const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa`;\r\n\r\n const response = await fetch(url, {\r\n method: 'DELETE',\r\n headers: { authorization: token },\r\n });\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({})) as Record<string, unknown>;\r\n throw new ApiError(\r\n (errorData.message as string) || `HTTP error ${response.status}`,\r\n 'HTTP_ERROR',\r\n response.status\r\n );\r\n }\r\n\r\n const data = await response.json() as TwoFactorActionResponse;\r\n\r\n if (data.status === 0) {\r\n throw new ApiError(data.message || 'Erro ao desabilitar 2FA', 'DISABLE_2FA_FAILED', 400);\r\n }\r\n\r\n return data;\r\n }\r\n}\r\n\r\nexport const twoFactorService = new TwoFactorService();\r\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB;AACzB,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AAGpC,MAAM,kBAAkB,QAAQ,IAAI,cAAc;AAElD,MAAM,iBAAiB;AAAA,EACrB,MAAc,QAAW,MAAc,MAA2B;AAChE,UAAM,EAAE,IAAI,MAAM,IAAI,MAAM,oBAAoB;AAChD,UAAM,EAAE,YAAY,QAAQ,IAAI,MAAM,eAAe;AAErD,UAAM,MAAM,GAAG,eAAe,OAAO,EAAE,YAAY,UAAU,UAAU,OAAO,QAAQ,IAAI;AAE1F,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACxD,YAAM,IAAI;AAAA,QACP,UAAU,WAAsB,cAAc,SAAS,MAAM;AAAA,QAC9D;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA,EAEA,MAAM,WAA+C;AACnD,UAAM,WAAW,MAAM,KAAK,QAAmC,UAAU;AAEzE,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,SAAS,SAAS,WAAW,yBAAyB,uBAAuB,GAAG;AAAA,IAC5F;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,MAAc,SAAiE;AAC3F,UAAM,OAAO,SAAS,WAAW,SAAY,kBAAkB,QAAQ,MAAM,KAAK;AAClF,UAAM,WAAW,MAAM,KAAK,QAAiC,MAAM,EAAE,KAAK,CAAC;AAE3E,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,SAAS,SAAS,WAAW,yBAAmB,sBAAsB,GAAG;AAAA,IACrF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,MAAgD;AAC3D,UAAM,WAAW,MAAM,KAAK,QAAiC,UAAU,EAAE,KAAK,CAAC;AAE/E,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,SAAS,SAAS,WAAW,yBAAmB,qBAAqB,GAAG;AAAA,IACpF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAA4C;AAChD,UAAM,EAAE,IAAI,MAAM,IAAI,MAAM,oBAAoB;AAChD,UAAM,EAAE,YAAY,QAAQ,IAAI,MAAM,eAAe;AAErD,UAAM,MAAM,GAAG,eAAe,OAAO,EAAE,YAAY,UAAU,UAAU,OAAO;AAE9E,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,EAAE,eAAe,MAAM;AAAA,IAClC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACxD,YAAM,IAAI;AAAA,QACP,UAAU,WAAsB,cAAc,SAAS,MAAM;AAAA,QAC9D;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,SAAS,KAAK,WAAW,2BAA2B,sBAAsB,GAAG;AAAA,IACzF;AAEA,WAAO;AAAA,EACT;AACF;AAEO,MAAM,mBAAmB,IAAI,iBAAiB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/modules/users/schema.ts"],"sourcesContent":["export enum UserProfile {\r\n viewer = 'viewer',\r\n collaborator = 'collaborator',\r\n admin = 'admin',\r\n owner = 'owner',\r\n}\r\n\r\nexport interface User {\r\n // Identificação\r\n id: number;\r\n id_wl: number;\r\n id_account: number;\r\n id_api: number;\r\n\r\n // Dados pessoais\r\n name: string;\r\n last_name: string;\r\n email: string;\r\n ddi: string;\r\n phone: string;\r\n rg: string | null;\r\n cpf: string | null;\r\n gender: number;\r\n\r\n // Autenticação e segurança\r\n two_factor_authentication: boolean;\r\n google_sub?: string | null;\r\n google_linked_at?: string | null;\r\n\r\n // Perfil e permissões\r\n profile: UserProfile;\r\n\r\n // Preferências\r\n language: string;\r\n receive_sms: boolean;\r\n receive_email: boolean;\r\n photo: string | null;\r\n\r\n // Metadados\r\n deleted: number;\r\n datetime_alt: string;\r\n datetime_del: string | null;\r\n datetime_add: string;\r\n\r\n total_projects: number;\r\n projects: { id: number; title: string }[];\r\n\r\n initials?: string;\r\n emailVerified?: boolean;\r\n createdAt?: string;\r\n updatedAt?: string;\r\n}\r\n\r\nexport interface GetUserDataResponse {\r\n status: 0 | 1;\r\n total: number;\r\n data: User
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/users/schema.ts"],"sourcesContent":["export enum UserProfile {\r\n viewer = 'viewer',\r\n collaborator = 'collaborator',\r\n admin = 'admin',\r\n owner = 'owner',\r\n}\r\n\r\nexport interface User {\r\n // Identificação\r\n id: number;\r\n id_wl: number;\r\n id_account: number;\r\n id_api: number;\r\n\r\n // Dados pessoais\r\n name: string;\r\n last_name: string;\r\n email: string;\r\n ddi: string;\r\n phone: string;\r\n rg: string | null;\r\n cpf: string | null;\r\n gender: number;\r\n\r\n // Autenticação e segurança\r\n two_factor_authentication: boolean;\r\n google_sub?: string | null;\r\n google_linked_at?: string | null;\r\n\r\n // Perfil e permissões\r\n profile: UserProfile;\r\n\r\n // Preferências\r\n language: string;\r\n receive_sms: boolean;\r\n receive_email: boolean;\r\n photo: string | null;\r\n\r\n // Metadados\r\n deleted: number;\r\n datetime_alt: string;\r\n datetime_del: string | null;\r\n datetime_add: string;\r\n\r\n total_projects: number;\r\n projects: { id: number; title: string }[];\r\n\r\n initials?: string;\r\n emailVerified?: boolean;\r\n createdAt?: string;\r\n updatedAt?: string;\r\n}\r\n\r\nexport interface GetUserDataResponse {\r\n status: 0 | 1;\r\n total: number;\r\n data: Array<User & { password?: string }>;\r\n}\r\n\r\nexport interface JWTPayload {\r\n session: {\r\n location: {\r\n continent: string;\r\n country: string;\r\n region: string;\r\n city: string;\r\n latitude: string;\r\n longitude: string;\r\n };\r\n ip: string;\r\n timezone: string;\r\n agent: string;\r\n code: string | null;\r\n };\r\n id_wl: string;\r\n id_account: number;\r\n id_user: number;\r\n iss: string;\r\n aud: string;\r\n iat: number;\r\n}\r\n"],"mappings":"AAAO,IAAK,cAAL,kBAAKA,iBAAL;AACL,EAAAA,aAAA,YAAS;AACT,EAAAA,aAAA,kBAAe;AACf,EAAAA,aAAA,WAAQ;AACR,EAAAA,aAAA,WAAQ;AAJE,SAAAA;AAAA,GAAA;","names":["UserProfile"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cookies } from "next/headers";
|
|
2
2
|
import { ApiError } from "../../../infra/api/types";
|
|
3
3
|
import { decodeJwtPayload } from "../../../utils/jwt-payload";
|
|
4
|
-
import {
|
|
4
|
+
import { findWhitelabelToken } from "../../whitelabel/utils/find-whitelabel-token";
|
|
5
5
|
import { buildWlOverride } from "../../auth/utils/build-wl-override";
|
|
6
6
|
const AUTH_COOKIE_NAME = "greatapps";
|
|
7
7
|
const API_BASE_URL = process.env.GAPPS_R3_API_URL || "https://r3-api.greatapps.dev.br";
|
|
@@ -27,7 +27,7 @@ class UserService {
|
|
|
27
27
|
const payload = this.decodeJWT(token);
|
|
28
28
|
const { id_wl, id_account, id_user } = payload;
|
|
29
29
|
const wlOverride = overrides ? void 0 : await buildWlOverride(Number(id_wl));
|
|
30
|
-
const wlToken = overrides?.authToken ?? wlOverride?.authToken ?? (await
|
|
30
|
+
const wlToken = overrides?.authToken ?? wlOverride?.authToken ?? (await findWhitelabelToken()).token;
|
|
31
31
|
const url = `${API_BASE_URL}/v1/pt-br/${id_wl}/accounts/${id_account}/users/${id_user}`;
|
|
32
32
|
const response = await fetch(url, {
|
|
33
33
|
method: "GET",
|
|
@@ -43,7 +43,8 @@ class UserService {
|
|
|
43
43
|
if (data.status === 0 || !data.data || data.data.length === 0) {
|
|
44
44
|
throw new ApiError("Dados do usu\xE1rio n\xE3o encontrados", "USER_NOT_FOUND", 404);
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
const { password, ...user } = data.data[0];
|
|
47
|
+
return user;
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
const userService = new UserService();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/users/services/user.service.ts"],"sourcesContent":["\r\nimport { cookies } from 'next/headers';\r\n\r\nimport { ApiError } from '../../../infra/api/types';\r\nimport { decodeJwtPayload } from '../../../utils/jwt-payload';\r\nimport { GetUserDataResponse, JWTPayload, User } from '../schema';\r\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/users/services/user.service.ts"],"sourcesContent":["\r\nimport { cookies } from 'next/headers';\r\n\r\nimport { ApiError } from '../../../infra/api/types';\r\nimport { decodeJwtPayload } from '../../../utils/jwt-payload';\r\nimport { GetUserDataResponse, JWTPayload, User } from '../schema';\r\nimport { findWhitelabelToken } from '../../whitelabel/utils/find-whitelabel-token';\r\nimport { buildWlOverride } from '../../auth/utils/build-wl-override';\r\n\r\nconst AUTH_COOKIE_NAME = 'greatapps';\r\nconst API_BASE_URL = process.env.GAPPS_R3_API_URL || 'https://r3-api.greatapps.dev.br';\r\n\r\nclass UserService {\r\n private async getAuthToken(): Promise<string> {\r\n if (process.env.DUMMY_AUTH_TOKEN) return process.env.DUMMY_AUTH_TOKEN;\r\n const cookieStore = await cookies();\r\n const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;\r\n\r\n if (!token) {\r\n throw new ApiError('Não autenticado', 'NOT_AUTHENTICATED_GAT', 401);\r\n }\r\n\r\n return token;\r\n }\r\n\r\n private decodeJWT(token: string): JWTPayload {\r\n try {\r\n return decodeJwtPayload<JWTPayload>(token);\r\n } catch {\r\n throw new ApiError('Token inválido', 'INVALID_TOKEN', 401);\r\n }\r\n }\r\n\r\n async findById(overrides?: { authToken?: string }): Promise<User> {\r\n const token = await this.getAuthToken();\r\n const payload = this.decodeJWT(token);\r\n\r\n const { id_wl, id_account, id_user } = payload;\r\n const wlOverride = overrides ? undefined : await buildWlOverride(Number(id_wl));\r\n const wlToken = overrides?.authToken ?? wlOverride?.authToken ?? (await findWhitelabelToken()).token;\r\n\r\n const url = `${API_BASE_URL}/v1/pt-br/${id_wl}/accounts/${id_account}/users/${id_user}`;\r\n\r\n const response = await fetch(url, {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n authorization: wlToken,\r\n },\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ApiError('Erro ao buscar dados do usuário', 'FETCH_ERROR', response.status);\r\n }\r\n\r\n const data = (await response.json()) as GetUserDataResponse;\r\n\r\n if (data.status === 0 || !data.data || data.data.length === 0) {\r\n throw new ApiError('Dados do usuário não encontrados', 'USER_NOT_FOUND', 404);\r\n }\r\n\r\n const { password, ...user } = data.data[0];\r\n\r\n return user;\r\n }\r\n}\r\n\r\nexport const userService = new UserService();\r\n"],"mappings":"AACA,SAAS,eAAe;AAExB,SAAS,gBAAgB;AACzB,SAAS,wBAAwB;AAEjC,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAEhC,MAAM,mBAAmB;AACzB,MAAM,eAAe,QAAQ,IAAI,oBAAoB;AAErD,MAAM,YAAY;AAAA,EAChB,MAAc,eAAgC;AAC5C,QAAI,QAAQ,IAAI,iBAAkB,QAAO,QAAQ,IAAI;AACrD,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,QAAQ,YAAY,IAAI,gBAAgB,GAAG;AAEjD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,SAAS,sBAAmB,yBAAyB,GAAG;AAAA,IACpE;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAA2B;AAC3C,QAAI;AACF,aAAO,iBAA6B,KAAK;AAAA,IAC3C,QAAQ;AACN,YAAM,IAAI,SAAS,qBAAkB,iBAAiB,GAAG;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,WAAmD;AAChE,UAAM,QAAQ,MAAM,KAAK,aAAa;AACtC,UAAM,UAAU,KAAK,UAAU,KAAK;AAEpC,UAAM,EAAE,OAAO,YAAY,QAAQ,IAAI;AACvC,UAAM,aAAa,YAAY,SAAY,MAAM,gBAAgB,OAAO,KAAK,CAAC;AAC9E,UAAM,UAAU,WAAW,aAAa,YAAY,cAAc,MAAM,oBAAoB,GAAG;AAE/F,UAAM,MAAM,GAAG,YAAY,aAAa,KAAK,aAAa,UAAU,UAAU,OAAO;AAErF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,SAAS,sCAAmC,eAAe,SAAS,MAAM;AAAA,IACtF;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAElC,QAAI,KAAK,WAAW,KAAK,CAAC,KAAK,QAAQ,KAAK,KAAK,WAAW,GAAG;AAC7D,YAAM,IAAI,SAAS,0CAAoC,kBAAkB,GAAG;AAAA,IAC9E;AAEA,UAAM,EAAE,UAAU,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC;AAEzC,WAAO;AAAA,EACT;AACF;AAEO,MAAM,cAAc,IAAI,YAAY;","names":[]}
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
"use server";
|
|
2
|
-
import {
|
|
3
|
-
import { whitelabelService } from "../services/whitelabel.service";
|
|
4
|
-
import { ApiError } from "../../../infra/api/types";
|
|
5
|
-
import { cache } from "react";
|
|
6
|
-
const findWhitelabelByHost = cache(async (host) => {
|
|
7
|
-
return whitelabelService.getTokenByDomain(host);
|
|
8
|
-
});
|
|
2
|
+
import { resolveWhitelabelForHost } from "../utils/resolve-whitelabel-for-host";
|
|
9
3
|
const findWhitelabel = async () => {
|
|
10
|
-
const
|
|
11
|
-
const host = headersList.get("host");
|
|
12
|
-
if (!host) {
|
|
13
|
-
throw new ApiError("Host header not found", "HOST_NOT_FOUND", 400);
|
|
14
|
-
}
|
|
15
|
-
const whitelabel = await findWhitelabelByHost(host);
|
|
16
|
-
Object.defineProperty(whitelabel, "token", { enumerable: false });
|
|
4
|
+
const { token, ...whitelabel } = await resolveWhitelabelForHost();
|
|
17
5
|
return whitelabel;
|
|
18
6
|
};
|
|
19
7
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/whitelabel/actions/find-whitelabel.action.ts"],"sourcesContent":["'use server';\n\
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/whitelabel/actions/find-whitelabel.action.ts"],"sourcesContent":["'use server';\n\nimport { resolveWhitelabelForHost } from '../utils/resolve-whitelabel-for-host';\n\nexport const findWhitelabel = async () => {\n const { token, ...whitelabel } = await resolveWhitelabelForHost();\n\n return whitelabel;\n};\n"],"mappings":";AAEA,SAAS,gCAAgC;AAElC,MAAM,iBAAiB,YAAY;AACtC,QAAM,EAAE,OAAO,GAAG,WAAW,IAAI,MAAM,yBAAyB;AAEhE,SAAO;AACX;","names":[]}
|
|
@@ -10,8 +10,7 @@ async function revalidateWhitelabelAction() {
|
|
|
10
10
|
if (!host) {
|
|
11
11
|
throw new ApiError("Host header not found", "HOST_NOT_FOUND", 400);
|
|
12
12
|
}
|
|
13
|
-
const whitelabel = await whitelabelService.revalidateByDomain(host);
|
|
14
|
-
Object.defineProperty(whitelabel, "token", { enumerable: false });
|
|
13
|
+
const { token, ...whitelabel } = await whitelabelService.revalidateByDomain(host);
|
|
15
14
|
return whitelabel;
|
|
16
15
|
});
|
|
17
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/whitelabel/actions/revalidate-whitelabel.action.ts"],"sourcesContent":["'use server';\n\nimport { headers } from 'next/headers';\nimport { whitelabelService } from '../services/whitelabel.service';\nimport { ApiError } from '../../../infra/api/types';\nimport { safeServerAction } from '../../../utils/safeServerAction';\n\nexport async function revalidateWhitelabelAction() {\n return safeServerAction(async () => {\n const headersList = await headers();\n const host = headersList.get('host');\n\n if (!host) {\n throw new ApiError('Host header not found', 'HOST_NOT_FOUND', 400);\n }\n\n const whitelabel = await whitelabelService.revalidateByDomain(host);\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/whitelabel/actions/revalidate-whitelabel.action.ts"],"sourcesContent":["'use server';\n\nimport { headers } from 'next/headers';\nimport { whitelabelService } from '../services/whitelabel.service';\nimport { ApiError } from '../../../infra/api/types';\nimport { safeServerAction } from '../../../utils/safeServerAction';\n\nexport async function revalidateWhitelabelAction() {\n return safeServerAction(async () => {\n const headersList = await headers();\n const host = headersList.get('host');\n\n if (!host) {\n throw new ApiError('Host header not found', 'HOST_NOT_FOUND', 400);\n }\n\n const { token, ...whitelabel } = await whitelabelService.revalidateByDomain(host);\n\n return whitelabel;\n });\n}\n"],"mappings":";AAEA,SAAS,eAAe;AACxB,SAAS,yBAAyB;AAClC,SAAS,gBAAgB;AACzB,SAAS,wBAAwB;AAEjC,eAAsB,6BAA6B;AACjD,SAAO,iBAAiB,YAAY;AAClC,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,OAAO,YAAY,IAAI,MAAM;AAEnC,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,SAAS,yBAAyB,kBAAkB,GAAG;AAAA,IACnE;AAEA,UAAM,EAAE,OAAO,GAAG,WAAW,IAAI,MAAM,kBAAkB,mBAAmB,IAAI;AAEhF,WAAO;AAAA,EACT,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { resolveWhitelabelForHost } from "./resolve-whitelabel-for-host";
|
|
3
|
+
async function findWhitelabelToken() {
|
|
4
|
+
const { id, token } = await resolveWhitelabelForHost();
|
|
5
|
+
return { id, token };
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
findWhitelabelToken
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=find-whitelabel-token.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/whitelabel/utils/find-whitelabel-token.ts"],"sourcesContent":["import 'server-only';\n\nimport { resolveWhitelabelForHost } from './resolve-whitelabel-for-host';\n\n/**\n * Único ponto de leitura do token de serviço do whitelabel. Server-only por construção: sem\n * `'use server'`, o Next não gera endpoint algum para esta função, então ela nunca é chamável\n * pelo client.\n */\nexport async function findWhitelabelToken(): Promise<{ id: number; token: string }> {\n const { id, token } = await resolveWhitelabelForHost();\n\n return { id, token };\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gCAAgC;AAOzC,eAAsB,sBAA8D;AAClF,QAAM,EAAE,IAAI,MAAM,IAAI,MAAM,yBAAyB;AAErD,SAAO,EAAE,IAAI,MAAM;AACrB;","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { headers } from "next/headers";
|
|
3
|
+
import { cache } from "react";
|
|
4
|
+
import { whitelabelService } from "../services/whitelabel.service";
|
|
5
|
+
import { ApiError } from "../../../infra/api/types";
|
|
6
|
+
const resolveWhitelabelByHost = cache(async (host) => {
|
|
7
|
+
return whitelabelService.getTokenByDomain(host);
|
|
8
|
+
});
|
|
9
|
+
async function resolveWhitelabelForHost() {
|
|
10
|
+
const headersList = await headers();
|
|
11
|
+
const host = headersList.get("host");
|
|
12
|
+
if (!host) {
|
|
13
|
+
throw new ApiError("Host header not found", "HOST_NOT_FOUND", 400);
|
|
14
|
+
}
|
|
15
|
+
return resolveWhitelabelByHost(host);
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
resolveWhitelabelForHost
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=resolve-whitelabel-for-host.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/whitelabel/utils/resolve-whitelabel-for-host.ts"],"sourcesContent":["import 'server-only';\n\nimport { headers } from 'next/headers';\nimport { cache } from 'react';\nimport { whitelabelService } from '../services/whitelabel.service';\nimport { ApiError } from '../../../infra/api/types';\nimport type { WhitelabelTokenData } from '../schema';\n\nconst resolveWhitelabelByHost = cache(async (host: string): Promise<WhitelabelTokenData> => {\n return whitelabelService.getTokenByDomain(host);\n});\n\n/**\n * Resolução por host da requisição atual, deduplicada via React.cache. Base compartilhada de\n * `findWhitelabel()` (sem token) e `findWhitelabelToken()` (com token) — as duas leem o mesmo\n * whitelabel resolvido, uma vez por request.\n */\nexport async function resolveWhitelabelForHost(): Promise<WhitelabelTokenData> {\n const headersList = await headers();\n const host = headersList.get('host');\n\n if (!host) {\n throw new ApiError('Host header not found', 'HOST_NOT_FOUND', 400);\n }\n\n return resolveWhitelabelByHost(host);\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,yBAAyB;AAClC,SAAS,gBAAgB;AAGzB,MAAM,0BAA0B,MAAM,OAAO,SAA+C;AAC1F,SAAO,kBAAkB,iBAAiB,IAAI;AAChD,CAAC;AAOD,eAAsB,2BAAyD;AAC7E,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,OAAO,YAAY,IAAI,MAAM;AAEnC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,SAAS,yBAAyB,kBAAkB,GAAG;AAAA,EACnE;AAEA,SAAO,wBAAwB,IAAI;AACrC;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/query.provider.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport {\r\n QueryCache,\r\n QueryClient,\r\n QueryClientProvider,\r\n} from \"@tanstack/react-query\";\r\nimport { ReactNode, useEffect, useState } from \"react\";\r\nimport dynamic from \"next/dynamic\";\r\nimport { toast } from \"sonner\";\r\nimport {\r\n PersistedClient,\r\n PersistQueryClientProvider,\r\n} from \"@tanstack/react-query-persist-client\";\r\nimport { set, get, del } from \"idb-keyval\";\r\nimport { isShareableQuery, shareQueryCacheBetweenTabs } from \"./share-query-cache\";\r\n\r\n// React Query Devtools só em desenvolvimento - remove do bundle de produção\r\nconst ReactQueryDevtools =\r\n process.env.NODE_ENV === \"development\"\r\n ? dynamic(\r\n () =>\r\n import(\"@tanstack/react-query-devtools\").then((mod) => ({\r\n default: mod.ReactQueryDevtools,\r\n })),\r\n { ssr: false },\r\n )\r\n : () => null;\r\n\r\nexport const persister = {\r\n persistClient: async (client: PersistedClient) => {\r\n try {\r\n await set(\"rq-cache\", client);\r\n } catch {\r\n console.warn(\"Falha ao persistir cache\");\r\n }\r\n },\r\n restoreClient: async (): Promise<PersistedClient | undefined> => {\r\n try {\r\n return await get(\"rq-cache\");\r\n } catch {\r\n await del(\"rq-cache\").catch(() => {});\r\n return undefined;\r\n }\r\n },\r\n removeClient: async () => {\r\n try {\r\n await del(\"rq-cache\");\r\n } catch {\r\n console.warn(\"Falha ao remover cache\");\r\n }\r\n },\r\n};\r\n\r\ninterface QueryProviderProps {\r\n children: ReactNode;\r\n persisterVersion?: string;\r\n}\r\n\r\nexport function QueryProvider({\r\n children,\r\n persisterVersion = \"
|
|
1
|
+
{"version":3,"sources":["../../src/providers/query.provider.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport {\r\n QueryCache,\r\n QueryClient,\r\n QueryClientProvider,\r\n} from \"@tanstack/react-query\";\r\nimport { ReactNode, useEffect, useState } from \"react\";\r\nimport dynamic from \"next/dynamic\";\r\nimport { toast } from \"sonner\";\r\nimport {\r\n PersistedClient,\r\n PersistQueryClientProvider,\r\n} from \"@tanstack/react-query-persist-client\";\r\nimport { set, get, del } from \"idb-keyval\";\r\nimport { isShareableQuery, shareQueryCacheBetweenTabs } from \"./share-query-cache\";\r\n\r\n// React Query Devtools só em desenvolvimento - remove do bundle de produção\r\nconst ReactQueryDevtools =\r\n process.env.NODE_ENV === \"development\"\r\n ? dynamic(\r\n () =>\r\n import(\"@tanstack/react-query-devtools\").then((mod) => ({\r\n default: mod.ReactQueryDevtools,\r\n })),\r\n { ssr: false },\r\n )\r\n : () => null;\r\n\r\nexport const persister = {\r\n persistClient: async (client: PersistedClient) => {\r\n try {\r\n await set(\"rq-cache\", client);\r\n } catch {\r\n console.warn(\"Falha ao persistir cache\");\r\n }\r\n },\r\n restoreClient: async (): Promise<PersistedClient | undefined> => {\r\n try {\r\n return await get(\"rq-cache\");\r\n } catch {\r\n await del(\"rq-cache\").catch(() => {});\r\n return undefined;\r\n }\r\n },\r\n removeClient: async () => {\r\n try {\r\n await del(\"rq-cache\");\r\n } catch {\r\n console.warn(\"Falha ao remover cache\");\r\n }\r\n },\r\n};\r\n\r\ninterface QueryProviderProps {\r\n children: ReactNode;\r\n persisterVersion?: string;\r\n}\r\n\r\nexport function QueryProvider({\r\n children,\r\n persisterVersion = \"v2\",\r\n}: QueryProviderProps) {\r\n const [queryClient] = useState(\r\n () =>\r\n new QueryClient({\r\n defaultOptions: {\r\n queries: {\r\n staleTime: 15 * 1000,\r\n gcTime: 5 * 60 * 1000,\r\n refetchOnWindowFocus: false,\r\n refetchOnReconnect: true,\r\n retry: 2,\r\n retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),\r\n },\r\n mutations: {\r\n retry: false,\r\n },\r\n },\r\n }),\r\n );\r\n\r\n useEffect(() => shareQueryCacheBetweenTabs(queryClient), [queryClient]);\r\n\r\n return (\r\n <PersistQueryClientProvider\r\n client={queryClient}\r\n persistOptions={{\r\n persister,\r\n maxAge: 2 * 60 * 60 * 1000,\r\n buster: persisterVersion,\r\n dehydrateOptions: {\r\n shouldDehydrateQuery: isShareableQuery,\r\n },\r\n }}\r\n >\r\n {children}\r\n <ReactQueryDevtools initialIsOpen={false} />\r\n </PersistQueryClientProvider>\r\n );\r\n}\r\n"],"mappings":";AAqFI,SAYE,KAZF;AAnFJ;AAAA,EAEE;AAAA,OAEK;AACP,SAAoB,WAAW,gBAAgB;AAC/C,OAAO,aAAa;AAEpB;AAAA,EAEE;AAAA,OACK;AACP,SAAS,KAAK,KAAK,WAAW;AAC9B,SAAS,kBAAkB,kCAAkC;AAG7D,MAAM,qBACJ,QAAQ,IAAI,aAAa,gBACrB;AAAA,EACE,MACE,OAAO,gCAAgC,EAAE,KAAK,CAAC,SAAS;AAAA,IACtD,SAAS,IAAI;AAAA,EACf,EAAE;AAAA,EACJ,EAAE,KAAK,MAAM;AACf,IACA,MAAM;AAEL,MAAM,YAAY;AAAA,EACvB,eAAe,OAAO,WAA4B;AAChD,QAAI;AACF,YAAM,IAAI,YAAY,MAAM;AAAA,IAC9B,QAAQ;AACN,cAAQ,KAAK,0BAA0B;AAAA,IACzC;AAAA,EACF;AAAA,EACA,eAAe,YAAkD;AAC/D,QAAI;AACF,aAAO,MAAM,IAAI,UAAU;AAAA,IAC7B,QAAQ;AACN,YAAM,IAAI,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACpC,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,cAAc,YAAY;AACxB,QAAI;AACF,YAAM,IAAI,UAAU;AAAA,IACtB,QAAQ;AACN,cAAQ,KAAK,wBAAwB;AAAA,IACvC;AAAA,EACF;AACF;AAOO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,mBAAmB;AACrB,GAAuB;AACrB,QAAM,CAAC,WAAW,IAAI;AAAA,IACpB,MACE,IAAI,YAAY;AAAA,MACd,gBAAgB;AAAA,QACd,SAAS;AAAA,UACP,WAAW,KAAK;AAAA,UAChB,QAAQ,IAAI,KAAK;AAAA,UACjB,sBAAsB;AAAA,UACtB,oBAAoB;AAAA,UACpB,OAAO;AAAA,UACP,YAAY,CAAC,YAAY,KAAK,IAAI,MAAO,KAAK,SAAS,GAAK;AAAA,QAC9D;AAAA,QACA,WAAW;AAAA,UACT,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,YAAU,MAAM,2BAA2B,WAAW,GAAG,CAAC,WAAW,CAAC;AAEtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ;AAAA,MACR,gBAAgB;AAAA,QACd;AAAA,QACA,QAAQ,IAAI,KAAK,KAAK;AAAA,QACtB,QAAQ;AAAA,QACR,kBAAkB;AAAA,UAChB,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,QACD,oBAAC,sBAAmB,eAAe,OAAO;AAAA;AAAA;AAAA,EAC5C;AAEJ;","names":[]}
|
package/dist/server.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiClient, api, apiClient } from "./infra/api/client";
|
|
|
3
3
|
import { authService } from "./modules/auth/services/auth.service";
|
|
4
4
|
import { whitelabelService } from "./modules/whitelabel/services/whitelabel.service";
|
|
5
5
|
import { revalidateWhitelabelAction } from "./modules/whitelabel/actions/revalidate-whitelabel.action";
|
|
6
|
+
import { findWhitelabelToken } from "./modules/whitelabel/utils/find-whitelabel-token";
|
|
6
7
|
import { assertManagementPermission } from "./modules/auth/utils/assert-management-permission";
|
|
7
8
|
import { assertManagementSubscription } from "./modules/auth/utils/assert-management-subscription";
|
|
8
9
|
import { assertSubscriptionAddon } from "./modules/auth/utils/assert-subscription-addon";
|
|
@@ -114,6 +115,7 @@ export {
|
|
|
114
115
|
findProjectAction,
|
|
115
116
|
findUserById,
|
|
116
117
|
findWhitelabel,
|
|
118
|
+
findWhitelabelToken,
|
|
117
119
|
generateTwoFactorAction,
|
|
118
120
|
getAccountTokenAction,
|
|
119
121
|
getClientInfoFromRequest,
|
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\n\n// API Client\nexport { ApiClient, api, apiClient } from './infra/api/client';\n\n// Services\nexport { authService } from './modules/auth/services/auth.service';\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\nexport { assertAccountNotFrozen } from './modules/auth/utils/assert-account-not-frozen';\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\nexport { plansService } from './modules/plans/services/plans.service';\nexport { cardsService } from './modules/cards/services/cards.service';\nexport { chargesService } from './modules/charges/services/charges.service';\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\n\n// Actions\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\nexport { socialGoogleLoginAction } from './modules/auth/actions/social-google-login.action';\nexport { socialGoogleOnboardingAction } from './modules/auth/actions/social-google-onboarding.action';\nexport { unlinkGoogleAction } from './modules/auth/actions/unlink-google.action';\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\nexport { getCurrentSubscriptionAction } from './modules/subscriptions/actions/get-current-subscription.action';\nexport { readCurrentSubscription } from './modules/subscriptions/utils/read-current-subscription';\nexport { ackFreezeNoticeAction } from './modules/subscriptions/actions/ack-freeze-notice.action';\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\nexport { createCardAction } from './modules/cards/actions/create-card.action';\nexport { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\nexport { hasActivePaidSubscription } from './modules/subscriptions/utils/has-active-paid-subscription';\n\n// Project Services & Actions (server-only)\nexport { projectsService } from './modules/projects/services/projects.service';\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\n\n// Project Users Services & Actions (server-only)\nexport { projectUsersService } from './modules/projects/services/project-users.service';\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\n\n// Account Services & Actions (server-only)\nexport { accountService } from './modules/accounts/services/account.service';\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\nexport {\n listAccountUsersAction,\n updateUserAction,\n updateAccountAction,\n updateAccountUserByIdAction,\n updateBillingDataAction,\n deleteAccountUserAction,\n deleteAccountAction,\n createAccountUserAction,\n checkUserExistsAction,\n addUserProjectsAction,\n removeUserProjectsAction,\n changePasswordAction,\n requestEmailChangeAction,\n confirmEmailChangeAction,\n requestPhoneChangeAction,\n confirmPhoneChangeAction,\n generateTwoFactorAction,\n confirmTwoFactorAction,\n disableTwoFactorAction,\n} from './modules/accounts/actions/account-management.action';\n\n// Server Utils\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\nexport { getUserContext } from './modules/auth/utils/get-user-context';\nexport { resolveLocale } from './i18n/resolve-locale';\nexport type { ResolveLocaleOptions } from './i18n/resolve-locale';\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\nexport { safeServerAction } from './utils/safeServerAction';\nexport { safeMutationAction } from './utils/safeMutationAction';\n\n// Image Processing (server-side)\nexport { processImageAction } from './modules/images/actions/process-image.action';\nexport { processImage } from './modules/images/services/image-processing.service';\nexport type {\n ProcessImageInput,\n ProcessImageOutput,\n ProcessImageActionInput,\n ProcessImageActionOutput,\n} from './modules/images/types/image.type';\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAG1C,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAE9B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\n\n// API Client\nexport { ApiClient, api, apiClient } from './infra/api/client';\n\n// Services\nexport { authService } from './modules/auth/services/auth.service';\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\nexport { findWhitelabelToken } from './modules/whitelabel/utils/find-whitelabel-token';\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\nexport { assertAccountNotFrozen } from './modules/auth/utils/assert-account-not-frozen';\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\nexport { plansService } from './modules/plans/services/plans.service';\nexport { cardsService } from './modules/cards/services/cards.service';\nexport { chargesService } from './modules/charges/services/charges.service';\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\n\n// Actions\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\nexport { socialGoogleLoginAction } from './modules/auth/actions/social-google-login.action';\nexport { socialGoogleOnboardingAction } from './modules/auth/actions/social-google-onboarding.action';\nexport { unlinkGoogleAction } from './modules/auth/actions/unlink-google.action';\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\nexport { getCurrentSubscriptionAction } from './modules/subscriptions/actions/get-current-subscription.action';\nexport { readCurrentSubscription } from './modules/subscriptions/utils/read-current-subscription';\nexport { ackFreezeNoticeAction } from './modules/subscriptions/actions/ack-freeze-notice.action';\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\nexport { createCardAction } from './modules/cards/actions/create-card.action';\nexport { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\nexport { hasActivePaidSubscription } from './modules/subscriptions/utils/has-active-paid-subscription';\n\n// Project Services & Actions (server-only)\nexport { projectsService } from './modules/projects/services/projects.service';\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\n\n// Project Users Services & Actions (server-only)\nexport { projectUsersService } from './modules/projects/services/project-users.service';\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\n\n// Account Services & Actions (server-only)\nexport { accountService } from './modules/accounts/services/account.service';\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\nexport {\n listAccountUsersAction,\n updateUserAction,\n updateAccountAction,\n updateAccountUserByIdAction,\n updateBillingDataAction,\n deleteAccountUserAction,\n deleteAccountAction,\n createAccountUserAction,\n checkUserExistsAction,\n addUserProjectsAction,\n removeUserProjectsAction,\n changePasswordAction,\n requestEmailChangeAction,\n confirmEmailChangeAction,\n requestPhoneChangeAction,\n confirmPhoneChangeAction,\n generateTwoFactorAction,\n confirmTwoFactorAction,\n disableTwoFactorAction,\n} from './modules/accounts/actions/account-management.action';\n\n// Server Utils\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\nexport { getUserContext } from './modules/auth/utils/get-user-context';\nexport { resolveLocale } from './i18n/resolve-locale';\nexport type { ResolveLocaleOptions } from './i18n/resolve-locale';\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\nexport { safeServerAction } from './utils/safeServerAction';\nexport { safeMutationAction } from './utils/safeMutationAction';\n\n// Image Processing (server-side)\nexport { processImageAction } from './modules/images/actions/process-image.action';\nexport { processImage } from './modules/images/services/image-processing.service';\nexport type {\n ProcessImageInput,\n ProcessImageOutput,\n ProcessImageActionInput,\n ProcessImageActionOutput,\n} from './modules/images/types/image.type';\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAG1C,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAE9B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
|
package/package.json
CHANGED
package/src/infra/api/client.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { headers as nextHeaders } from "next/headers";
|
|
2
2
|
|
|
3
3
|
import { ApiError } from "./types";
|
|
4
|
-
import {
|
|
4
|
+
import { findWhitelabelToken } from "../../modules/whitelabel/utils/find-whitelabel-token";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Headers de tracing distribuído do Sentry, repassados da requisição que chegou neste worker
|
|
@@ -40,9 +40,9 @@ export interface RequestConfig extends RequestInit {
|
|
|
40
40
|
timeout?: number;
|
|
41
41
|
/** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */
|
|
42
42
|
responseType?: "json" | "text";
|
|
43
|
-
/** Força um whiteLabelId específico na URL, ignorando o
|
|
43
|
+
/** Força um whiteLabelId específico na URL, ignorando o findWhitelabelToken(). */
|
|
44
44
|
whiteLabelId?: number;
|
|
45
|
-
/** Força um token específico no header Authorization, ignorando o
|
|
45
|
+
/** Força um token específico no header Authorization, ignorando o findWhitelabelToken(). */
|
|
46
46
|
authToken?: string;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -99,7 +99,7 @@ class ApiClient {
|
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Repassa o tracing quando existe. Sem mecanismo novo: `request()` já aguarda
|
|
102
|
-
* `
|
|
102
|
+
* `findWhitelabelToken()`, que já lê `next/headers` — é a mesma chamada, no
|
|
103
103
|
* mesmo ponto do fluxo. Fica aqui dentro de propósito: os dois call sites de
|
|
104
104
|
* `getDefaultHeaders` passam a propagar sem precisar lembrar disso em cada um.
|
|
105
105
|
*
|
|
@@ -140,7 +140,7 @@ class ApiClient {
|
|
|
140
140
|
console.log("[ApiClient] Building URL and headers...");
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
const { id, token } = await
|
|
143
|
+
const { id, token } = await findWhitelabelToken();
|
|
144
144
|
|
|
145
145
|
const [url, defaultHeaders] = await Promise.all([
|
|
146
146
|
this.buildUrl(endpoint, overrideWlId ?? id),
|
|
@@ -314,7 +314,7 @@ class ApiClient {
|
|
|
314
314
|
// consulta seria uma ida à rede para descartar o resultado.
|
|
315
315
|
const resolvido = overrideWlId != null && overrideToken != null
|
|
316
316
|
? { id: overrideWlId, token: overrideToken }
|
|
317
|
-
: await
|
|
317
|
+
: await findWhitelabelToken();
|
|
318
318
|
|
|
319
319
|
const [url, defaultHeaders] = await Promise.all([
|
|
320
320
|
this.buildUrl(endpoint, overrideWlId ?? resolvido.id),
|
|
@@ -42,12 +42,20 @@ class AccountService {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
const { user, ...account } = response.data[0];
|
|
46
|
+
|
|
47
|
+
return account;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private pickUsersQueryParams(params: Partial<AccountUsersPaginationParams> = {}) {
|
|
51
|
+
const { page, limit, search, name, notInProject, profiles, firstId } = params;
|
|
52
|
+
|
|
53
|
+
return { page, limit, search, name, notInProject, profiles, firstId };
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
async listAccountUsers(params?: Partial<AccountUsersPaginationParams>) {
|
|
49
57
|
const { id_account } = await getUserContext();
|
|
50
|
-
const queryParams = buildQueryParams(params);
|
|
58
|
+
const queryParams = buildQueryParams(this.pickUsersQueryParams(params));
|
|
51
59
|
const endpoint = `/accounts/${id_account}/users?${queryParams}`;
|
|
52
60
|
const response = await api.apps.get<ListAccountUsersResponse>(endpoint);
|
|
53
61
|
|
|
@@ -71,7 +79,7 @@ class AccountService {
|
|
|
71
79
|
*/
|
|
72
80
|
async listAccountMembers(params?: Partial<AccountUsersPaginationParams>) {
|
|
73
81
|
const { id_account, id_user } = await getUserContext();
|
|
74
|
-
const queryParams = buildQueryParams({ ...params, scopeUserId: id_user });
|
|
82
|
+
const queryParams = buildQueryParams({ ...this.pickUsersQueryParams(params), scopeUserId: id_user });
|
|
75
83
|
const response = await api.apps.get<ListAccountUsersResponse>(
|
|
76
84
|
`/accounts/${id_account}/users?${queryParams}`
|
|
77
85
|
);
|
|
@@ -106,7 +114,7 @@ class AccountService {
|
|
|
106
114
|
);
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
return response;
|
|
117
|
+
return { status: response.status, message: response.message };
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
async deleteAccountUser(userId: number): Promise<DeleteAccountUserResponse> {
|
|
@@ -119,7 +127,7 @@ class AccountService {
|
|
|
119
127
|
throw new ApiError(response.message || 'Erro ao deletar usuário', 'DELETE_USER_FAILED', 400);
|
|
120
128
|
}
|
|
121
129
|
|
|
122
|
-
return response;
|
|
130
|
+
return { status: response.status, message: response.message };
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
async deleteAccount(data: DeleteAccountRequest): Promise<DeleteAccountResponse> {
|
|
@@ -150,7 +158,7 @@ class AccountService {
|
|
|
150
158
|
throw new ApiError(response.message || 'Erro ao criar usuário', 'CREATE_USER_FAILED', 400);
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
return response;
|
|
161
|
+
return { status: response.status, message: response.message };
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
private async resolvePhotoField(user: UpdateAccountUserRequest): Promise<{ photo?: string | null }> {
|
|
@@ -177,7 +185,7 @@ class AccountService {
|
|
|
177
185
|
);
|
|
178
186
|
}
|
|
179
187
|
|
|
180
|
-
return response;
|
|
188
|
+
return { status: response.status, message: response.message };
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
async updateAccountUserById(
|
|
@@ -198,7 +206,7 @@ class AccountService {
|
|
|
198
206
|
);
|
|
199
207
|
}
|
|
200
208
|
|
|
201
|
-
return response;
|
|
209
|
+
return { status: response.status, message: response.message };
|
|
202
210
|
}
|
|
203
211
|
|
|
204
212
|
async updateAccount(data: UpdateAccountRequest): Promise<UpdateAccountResponse> {
|
|
@@ -2,14 +2,14 @@ import 'server-only';
|
|
|
2
2
|
|
|
3
3
|
import { ApiError } from '../../../infra/api/types';
|
|
4
4
|
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
5
|
-
import {
|
|
5
|
+
import { findWhitelabelToken } from '../../whitelabel/utils/find-whitelabel-token';
|
|
6
6
|
import type { TwoFactorActionResponse, TwoFactorGenerateResponse } from '../types';
|
|
7
7
|
|
|
8
8
|
const TWO_FA_BASE_URL = process.env.R2_2FA_URL || 'https://r2-2fa.greatapps.dev.br';
|
|
9
9
|
|
|
10
10
|
class TwoFactorService {
|
|
11
11
|
private async request<T>(path: string, body?: object): Promise<T> {
|
|
12
|
-
const { id, token } = await
|
|
12
|
+
const { id, token } = await findWhitelabelToken();
|
|
13
13
|
const { id_account, id_user } = await getUserContext();
|
|
14
14
|
|
|
15
15
|
const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa/${path}`;
|
|
@@ -67,7 +67,7 @@ class TwoFactorService {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
async disable(): Promise<TwoFactorActionResponse> {
|
|
70
|
-
const { id, token } = await
|
|
70
|
+
const { id, token } = await findWhitelabelToken();
|
|
71
71
|
const { id_account, id_user } = await getUserContext();
|
|
72
72
|
|
|
73
73
|
const url = `${TWO_FA_BASE_URL}/v1/${id}/account/${id_account}/users/${id_user}/2fa`;
|
|
@@ -55,7 +55,7 @@ export interface AccountUsersPaginationParams {
|
|
|
55
55
|
export interface GetAccountDataResponse {
|
|
56
56
|
status: 0 | 1;
|
|
57
57
|
message: string;
|
|
58
|
-
data: Account
|
|
58
|
+
data: Array<Account & { user?: User }>;
|
|
59
59
|
total: number;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -92,7 +92,6 @@ export interface CreateAccountUserRequest {
|
|
|
92
92
|
export interface CreateAccountUserResponse {
|
|
93
93
|
status: 0 | 1;
|
|
94
94
|
message: string;
|
|
95
|
-
data: User[];
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
export interface UpdateAccountUserRequest {
|
|
@@ -118,7 +117,6 @@ export interface UpdateAccountUserRequest {
|
|
|
118
117
|
export interface UpdateAccountUserResponse {
|
|
119
118
|
status: 0 | 1;
|
|
120
119
|
message: string;
|
|
121
|
-
data: User[];
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
export interface ChangeUserPhotoResponse {
|
|
@@ -4,7 +4,7 @@ import { cookies } from 'next/headers';
|
|
|
4
4
|
import { ApiError } from '../../../infra/api/types';
|
|
5
5
|
import { decodeJwtPayload } from '../../../utils/jwt-payload';
|
|
6
6
|
import { GetUserDataResponse, JWTPayload, User } from '../schema';
|
|
7
|
-
import {
|
|
7
|
+
import { findWhitelabelToken } from '../../whitelabel/utils/find-whitelabel-token';
|
|
8
8
|
import { buildWlOverride } from '../../auth/utils/build-wl-override';
|
|
9
9
|
|
|
10
10
|
const AUTH_COOKIE_NAME = 'greatapps';
|
|
@@ -37,7 +37,7 @@ class UserService {
|
|
|
37
37
|
|
|
38
38
|
const { id_wl, id_account, id_user } = payload;
|
|
39
39
|
const wlOverride = overrides ? undefined : await buildWlOverride(Number(id_wl));
|
|
40
|
-
const wlToken = overrides?.authToken ?? wlOverride?.authToken ?? (await
|
|
40
|
+
const wlToken = overrides?.authToken ?? wlOverride?.authToken ?? (await findWhitelabelToken()).token;
|
|
41
41
|
|
|
42
42
|
const url = `${API_BASE_URL}/v1/pt-br/${id_wl}/accounts/${id_account}/users/${id_user}`;
|
|
43
43
|
|
|
@@ -59,7 +59,9 @@ class UserService {
|
|
|
59
59
|
throw new ApiError('Dados do usuário não encontrados', 'USER_NOT_FOUND', 404);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
const { password, ...user } = data.data[0];
|
|
63
|
+
|
|
64
|
+
return user;
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
'use server';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import { headers } from "next/headers";
|
|
5
|
-
import { whitelabelService } from "../services/whitelabel.service";
|
|
6
|
-
import { ApiError } from "../../../infra/api/types";
|
|
7
|
-
import { cache } from "react";
|
|
8
|
-
|
|
9
|
-
const findWhitelabelByHost = cache(async (host: string) => {
|
|
10
|
-
return whitelabelService.getTokenByDomain(host);
|
|
11
|
-
});
|
|
3
|
+
import { resolveWhitelabelForHost } from '../utils/resolve-whitelabel-for-host';
|
|
12
4
|
|
|
13
5
|
export const findWhitelabel = async () => {
|
|
14
|
-
const
|
|
15
|
-
const host = headersList.get('host');
|
|
16
|
-
|
|
17
|
-
if (!host) {
|
|
18
|
-
throw new ApiError('Host header not found', 'HOST_NOT_FOUND', 400);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const whitelabel = await findWhitelabelByHost(host);
|
|
22
|
-
Object.defineProperty(whitelabel, 'token', { enumerable: false });
|
|
6
|
+
const { token, ...whitelabel } = await resolveWhitelabelForHost();
|
|
23
7
|
|
|
24
8
|
return whitelabel;
|
|
25
9
|
};
|
|
@@ -14,8 +14,7 @@ export async function revalidateWhitelabelAction() {
|
|
|
14
14
|
throw new ApiError('Host header not found', 'HOST_NOT_FOUND', 400);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const whitelabel = await whitelabelService.revalidateByDomain(host);
|
|
18
|
-
Object.defineProperty(whitelabel, 'token', { enumerable: false });
|
|
17
|
+
const { token, ...whitelabel } = await whitelabelService.revalidateByDomain(host);
|
|
19
18
|
|
|
20
19
|
return whitelabel;
|
|
21
20
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { resolveWhitelabelForHost } from './resolve-whitelabel-for-host';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Único ponto de leitura do token de serviço do whitelabel. Server-only por construção: sem
|
|
7
|
+
* `'use server'`, o Next não gera endpoint algum para esta função, então ela nunca é chamável
|
|
8
|
+
* pelo client.
|
|
9
|
+
*/
|
|
10
|
+
export async function findWhitelabelToken(): Promise<{ id: number; token: string }> {
|
|
11
|
+
const { id, token } = await resolveWhitelabelForHost();
|
|
12
|
+
|
|
13
|
+
return { id, token };
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { headers } from 'next/headers';
|
|
4
|
+
import { cache } from 'react';
|
|
5
|
+
import { whitelabelService } from '../services/whitelabel.service';
|
|
6
|
+
import { ApiError } from '../../../infra/api/types';
|
|
7
|
+
import type { WhitelabelTokenData } from '../schema';
|
|
8
|
+
|
|
9
|
+
const resolveWhitelabelByHost = cache(async (host: string): Promise<WhitelabelTokenData> => {
|
|
10
|
+
return whitelabelService.getTokenByDomain(host);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolução por host da requisição atual, deduplicada via React.cache. Base compartilhada de
|
|
15
|
+
* `findWhitelabel()` (sem token) e `findWhitelabelToken()` (com token) — as duas leem o mesmo
|
|
16
|
+
* whitelabel resolvido, uma vez por request.
|
|
17
|
+
*/
|
|
18
|
+
export async function resolveWhitelabelForHost(): Promise<WhitelabelTokenData> {
|
|
19
|
+
const headersList = await headers();
|
|
20
|
+
const host = headersList.get('host');
|
|
21
|
+
|
|
22
|
+
if (!host) {
|
|
23
|
+
throw new ApiError('Host header not found', 'HOST_NOT_FOUND', 400);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return resolveWhitelabelByHost(host);
|
|
27
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { ApiClient, api, apiClient } from './infra/api/client';
|
|
|
7
7
|
export { authService } from './modules/auth/services/auth.service';
|
|
8
8
|
export { whitelabelService } from './modules/whitelabel/services/whitelabel.service';
|
|
9
9
|
export { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';
|
|
10
|
+
export { findWhitelabelToken } from './modules/whitelabel/utils/find-whitelabel-token';
|
|
10
11
|
export { assertManagementPermission } from './modules/auth/utils/assert-management-permission';
|
|
11
12
|
export { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';
|
|
12
13
|
export { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';
|