@greatapps/common 1.1.795 → 1.1.796

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.
Files changed (25) hide show
  1. package/dist/components/modals/billing/BillingDataForm.mjs +406 -0
  2. package/dist/components/modals/billing/BillingDataForm.mjs.map +1 -0
  3. package/dist/components/modals/billing/RequiredBillingDataModal.mjs +5 -405
  4. package/dist/components/modals/billing/RequiredBillingDataModal.mjs.map +1 -1
  5. package/dist/i18n/resolve-locale.mjs +1 -1
  6. package/dist/i18n/resolve-locale.mjs.map +1 -1
  7. package/dist/index.mjs +4 -0
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/modules/ia-credits/types/ai-credits-offer.type.mjs +3 -0
  10. package/dist/modules/ia-credits/types/ai-credits-offer.type.mjs.map +1 -1
  11. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs +1 -5
  12. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
  13. package/dist/modules/whitelabel/constants/whitelabel.constants.mjs +11 -0
  14. package/dist/modules/whitelabel/constants/whitelabel.constants.mjs.map +1 -0
  15. package/dist/modules/whitelabel/services/whitelabel.service.mjs +69 -11
  16. package/dist/modules/whitelabel/services/whitelabel.service.mjs.map +1 -1
  17. package/package.json +10 -9
  18. package/src/components/modals/billing/BillingDataForm.tsx +506 -0
  19. package/src/components/modals/billing/RequiredBillingDataModal.tsx +3 -475
  20. package/src/i18n/resolve-locale.ts +1 -1
  21. package/src/index.ts +3 -0
  22. package/src/modules/ia-credits/types/ai-credits-offer.type.ts +3 -0
  23. package/src/modules/whitelabel/actions/find-whitelabel.action.ts +1 -7
  24. package/src/modules/whitelabel/constants/whitelabel.constants.ts +14 -0
  25. package/src/modules/whitelabel/services/whitelabel.service.ts +282 -209
@@ -1,209 +1,282 @@
1
- import greatCache from "@greatapps/cache";
2
- import { ApiError } from "../../../infra/api/types";
3
- import { WhitelabelTokenApiResponse, WhitelabelTokenData } from "../schema";
4
- import { normalizeHostname } from "../utils/normalize-hostname";
5
-
6
- class WhitelabelService {
7
- private getApiUrl(): string {
8
- const apiUrl = process.env.GAPPS_R3_API_URL;
9
- if (!apiUrl) {
10
- throw new ApiError(
11
- "GAPPS_R3_API_URL not configured",
12
- "CONFIG_ERROR",
13
- 500,
14
- );
15
- }
16
- return apiUrl;
17
- }
18
-
19
- private getToken(): string {
20
- const token = process.env.WHITELABEL_TOKEN_MASTER;
21
- if (!token) {
22
- throw new ApiError(
23
- "WHITELABEL_TOKEN_MASTER not configured",
24
- "CONFIG_ERROR",
25
- 500,
26
- );
27
- }
28
- return token;
29
- }
30
-
31
- private createCache() {
32
- return new greatCache({
33
- service: "whitelabel-service",
34
- version: "1.0.3",
35
- domain: "whitelabel-cache.greatapps.com.br",
36
- ambient: process.env.NODE_ENV || "development",
37
- });
38
- }
39
-
40
- private getCacheKey(hostname: string): string {
41
- return `whitelabel-token-${hostname}`;
42
- }
43
-
44
- private async fetchFromApi(hostname: string): Promise<WhitelabelTokenData> {
45
- const apiUrl = this.getApiUrl();
46
- const whitelabelMasterToken = this.getToken();
47
- const url = `${apiUrl}/v1/pt-br/1/whitelabel/${hostname}/token`;
48
-
49
- console.log("[WhitelabelService] Fetching token for domain", { url });
50
-
51
- const response = await fetch(url, {
52
- method: "GET",
53
- headers: {
54
- authorization: whitelabelMasterToken,
55
- },
56
- });
57
-
58
- if (!response.ok) {
59
- console.error("[WhitelabelService] Failed to fetch whitelabel token", {
60
- response,
61
- });
62
- throw new ApiError(
63
- `Failed to fetch whitelabel token: ${response.status}`,
64
- "FETCH_ERROR",
65
- response.status,
66
- );
67
- }
68
-
69
- const result: WhitelabelTokenApiResponse = await response.json();
70
-
71
- if (result.status !== 1 || !result.data?.length) {
72
- throw new ApiError("Whitelabel token not found", "TOKEN_NOT_FOUND", 404);
73
- }
74
-
75
- return result.data[0];
76
- }
77
-
78
- async getTokenByDomain(hostname: string): Promise<WhitelabelTokenData> {
79
- hostname = `${normalizeHostname(process.env.WHITELABEL_DOMAIN || hostname)}`;
80
- console.debug("[WhitelabelService] Getting token for domain", {
81
- hostname,
82
- byEnv: process.env.WHITELABEL_DOMAIN,
83
- });
84
-
85
- const cache = this.createCache();
86
- const cacheKey = this.getCacheKey(hostname);
87
-
88
- const cachedData = await cache.select(cacheKey);
89
- console.debug("[WhitelabelService] Cache lookup for domain", {
90
- cacheKey,
91
- cachedData,
92
- });
93
-
94
- if (cachedData.status == 1 && "data" in cachedData && cachedData.data) {
95
- const cachedWhitelabel = JSON.parse(cachedData.data) as WhitelabelTokenData;
96
-
97
- if (this.#matchesHostname(cachedWhitelabel, hostname)) {
98
- console.log("[WhitelabelService] Cache hit for domain", { hostname });
99
- return cachedWhitelabel;
100
- }
101
-
102
- console.error("[WhitelabelService] Cached whitelabel does not match hostname", {
103
- hostname,
104
- cacheKey,
105
- whitelabelId: cachedWhitelabel.id,
106
- whitelabelDomain: cachedWhitelabel.domain,
107
- });
108
-
109
- await cache.delete(cacheKey);
110
- }
111
-
112
- const data = await this.fetchFromApi(hostname);
113
-
114
- if (!this.#matchesHostname(data, hostname)) {
115
- console.error("[WhitelabelService] API returned whitelabel of another hostname", {
116
- hostname,
117
- whitelabelId: data.id,
118
- whitelabelDomain: data.domain,
119
- });
120
- return data;
121
- }
122
-
123
- await cache.insert(cacheKey, JSON.stringify(data), 604800);
124
-
125
- return data;
126
- }
127
-
128
- #matchesHostname(data: WhitelabelTokenData, hostname: string): boolean {
129
- if (!data.domain) return true;
130
-
131
- try {
132
- return normalizeHostname(new URL(data.domain).hostname) === hostname;
133
- } catch {
134
- return true;
135
- }
136
- }
137
-
138
- async getTokenByWhitelabelId(idWl: number): Promise<string> {
139
- const apiUrl = this.getApiUrl();
140
- const masterToken = this.getToken();
141
- const url = `${apiUrl}/v1/pt-br/${idWl}/tokens?limit=1&page=1&sort=id:desc`;
142
- console.debug("[WhitelabelService] Fetching token by whitelabel ID", {
143
- url,
144
- config: {
145
- method: "GET",
146
- headers: { authorization: masterToken },
147
- },
148
- });
149
-
150
- const response = await fetch(url, {
151
- method: "GET",
152
- headers: { authorization: masterToken },
153
- });
154
-
155
- console.debug("[WhitelabelService] Response received for whitelabel ID", {
156
- response,
157
- });
158
-
159
- if (!response.ok) {
160
- throw new ApiError(
161
- `Failed to fetch whitelabel token: ${response.status}`,
162
- "WL_TOKEN_NOT_FOUND",
163
- response.status,
164
- );
165
- }
166
-
167
- const result: WhitelabelTokenApiResponse = await response.json();
168
-
169
- if (result.status !== 1 || !result.data?.length) {
170
- throw new ApiError(
171
- "Token do whitelabel não encontrado",
172
- "WL_TOKEN_NOT_FOUND",
173
- 404,
174
- );
175
- }
176
-
177
- return result.data[0].token;
178
- }
179
-
180
- async revalidateByDomain(hostname: string): Promise<WhitelabelTokenData> {
181
- hostname = `${normalizeHostname(process.env.WHITELABEL_DOMAIN || hostname)}`;
182
- console.debug("[WhitelabelService] Getting token for domain", {
183
- hostname,
184
- byEnv: process.env.WHITELABEL_DOMAIN,
185
- });
186
-
187
- const cache = this.createCache();
188
- const cacheKey = this.getCacheKey(hostname);
189
-
190
- console.log("[WhitelabelService] Revalidating cache for domain", {
191
- hostname,
192
- cacheKey,
193
- });
194
-
195
- await cache.delete(cacheKey);
196
-
197
- const data = await this.fetchFromApi(hostname);
198
-
199
- await cache.insert(cacheKey, JSON.stringify(data), 604800);
200
-
201
- console.log("[WhitelabelService] Cache revalidated for domain", {
202
- hostname,
203
- });
204
-
205
- return data;
206
- }
207
- }
208
-
209
- export const whitelabelService = new WhitelabelService();
1
+ import greatCache from "@greatapps/cache";
2
+ import { ApiError } from "../../../infra/api/types";
3
+ import { WhitelabelTokenApiResponse, WhitelabelTokenData } from "../schema";
4
+ import { normalizeHostname } from "../utils/normalize-hostname";
5
+ import {
6
+ DEFAULT_WHITELABEL_DOMAIN,
7
+ MISSING_WHITELABEL_CACHE_TTL_SECONDS,
8
+ WHITELABEL_CACHE_TTL_SECONDS,
9
+ } from "../constants/whitelabel.constants";
10
+
11
+ class WhitelabelService {
12
+ private getApiUrl(): string {
13
+ const apiUrl = process.env.GAPPS_R3_API_URL;
14
+ if (!apiUrl) {
15
+ throw new ApiError(
16
+ "GAPPS_R3_API_URL not configured",
17
+ "CONFIG_ERROR",
18
+ 500,
19
+ );
20
+ }
21
+ return apiUrl;
22
+ }
23
+
24
+ private getToken(): string {
25
+ const token = process.env.WHITELABEL_TOKEN_MASTER;
26
+ if (!token) {
27
+ throw new ApiError(
28
+ "WHITELABEL_TOKEN_MASTER not configured",
29
+ "CONFIG_ERROR",
30
+ 500,
31
+ );
32
+ }
33
+ return token;
34
+ }
35
+
36
+ private createCache() {
37
+ return new greatCache({
38
+ service: "whitelabel-service",
39
+ version: "1.0.3",
40
+ domain: "whitelabel-cache.greatapps.com.br",
41
+ ambient: process.env.NODE_ENV || "development",
42
+ });
43
+ }
44
+
45
+ private getCacheKey(hostname: string): string {
46
+ return `whitelabel-token-${hostname}`;
47
+ }
48
+
49
+ private getMissingCacheKey(hostname: string): string {
50
+ return `whitelabel-missing-${hostname}`;
51
+ }
52
+
53
+ private async fetchFromApi(hostname: string): Promise<WhitelabelTokenData | null> {
54
+ const apiUrl = this.getApiUrl();
55
+ const whitelabelMasterToken = this.getToken();
56
+ const url = `${apiUrl}/v1/pt-br/1/whitelabel/${hostname}/token`;
57
+
58
+ console.log("[WhitelabelService] Fetching token for domain", { url });
59
+
60
+ const response = await fetch(url, {
61
+ method: "GET",
62
+ headers: {
63
+ authorization: whitelabelMasterToken,
64
+ },
65
+ });
66
+
67
+ // Host sem whitelabel ativa é caso esperado (domínio novo, subdomínio livre) — não é falha de
68
+ // rede, então nem lê o corpo: cai no fallback do whitelabel padrão. 403 NÃO entra aqui: a rota
69
+ // emissora nunca responde 403, quem responde é borda (WAF, Access, rate limit) — tratar como
70
+ // ausência de whitelabel faria um incidente de borda derrubar toda whitelabel de cliente no
71
+ // whitelabel padrão, servindo branding e token de API errados em silêncio.
72
+ if (response.status === 404) {
73
+ console.log("[WhitelabelService] No whitelabel for domain", {
74
+ hostname,
75
+ status: response.status,
76
+ });
77
+ return null;
78
+ }
79
+
80
+ if (!response.ok) {
81
+ console.error("[WhitelabelService] Failed to fetch whitelabel token", {
82
+ hostname,
83
+ status: response.status,
84
+ });
85
+ throw new ApiError(
86
+ `Failed to fetch whitelabel token: ${response.status}`,
87
+ "FETCH_ERROR",
88
+ response.status,
89
+ );
90
+ }
91
+
92
+ const result: WhitelabelTokenApiResponse = await response.json();
93
+
94
+ if (result.status !== 1 || !result.data?.length) {
95
+ return null;
96
+ }
97
+
98
+ return result.data[0];
99
+ }
100
+
101
+ /**
102
+ * Cache (positivo) + fetch + validação de hostname pra um domínio já normalizado. Usado tanto
103
+ * pelo host da requisição quanto pelo domínio padrão — sem fallback embutido, pra `getDefault`
104
+ * não poder recursar nela mesma.
105
+ */
106
+ private async lookup(hostname: string): Promise<WhitelabelTokenData | null> {
107
+ const cache = this.createCache();
108
+ const cacheKey = this.getCacheKey(hostname);
109
+
110
+ const cachedData = await cache.select(cacheKey);
111
+ console.debug("[WhitelabelService] Cache lookup for domain", {
112
+ cacheKey,
113
+ cachedData,
114
+ });
115
+
116
+ if (cachedData.status == 1 && "data" in cachedData && cachedData.data) {
117
+ const cachedWhitelabel: WhitelabelTokenData = JSON.parse(cachedData.data);
118
+
119
+ if (this.#matchesHostname(cachedWhitelabel, hostname)) {
120
+ console.log("[WhitelabelService] Cache hit for domain", { hostname });
121
+ return cachedWhitelabel;
122
+ }
123
+
124
+ console.error("[WhitelabelService] Cached whitelabel does not match hostname", {
125
+ hostname,
126
+ cacheKey,
127
+ whitelabelId: cachedWhitelabel.id,
128
+ whitelabelDomain: cachedWhitelabel.domain,
129
+ });
130
+
131
+ await cache.delete(cacheKey);
132
+ }
133
+
134
+ const data = await this.fetchFromApi(hostname);
135
+ if (!data) return null;
136
+
137
+ if (!this.#matchesHostname(data, hostname)) {
138
+ console.error("[WhitelabelService] API returned whitelabel of another hostname", {
139
+ hostname,
140
+ whitelabelId: data.id,
141
+ whitelabelDomain: data.domain,
142
+ });
143
+ return data;
144
+ }
145
+
146
+ await cache.insert(cacheKey, JSON.stringify(data), WHITELABEL_CACHE_TTL_SECONDS);
147
+
148
+ return data;
149
+ }
150
+
151
+ /**
152
+ * Whitelabel padrão (Great), destino de todo host sem whitelabel própria. Resolve pelo mesmo
153
+ * `lookup`, então herda o cache positivo — mas nunca cai em `getTokenByDomain`, que teria
154
+ * fallback: aqui a ausência é falha de plataforma, não caso esperado.
155
+ */
156
+ private async getDefault(): Promise<WhitelabelTokenData> {
157
+ const domain = normalizeHostname(
158
+ process.env.WHITELABEL_DEFAULT_DOMAIN || DEFAULT_WHITELABEL_DOMAIN,
159
+ );
160
+
161
+ const data = await this.lookup(domain);
162
+
163
+ if (!data) {
164
+ throw new ApiError(
165
+ `Default whitelabel not found: ${domain}`,
166
+ "WL_DEFAULT_UNAVAILABLE",
167
+ 500,
168
+ );
169
+ }
170
+
171
+ return data;
172
+ }
173
+
174
+ async getTokenByDomain(hostname: string): Promise<WhitelabelTokenData> {
175
+ hostname = `${normalizeHostname(process.env.WHITELABEL_DOMAIN || hostname)}`;
176
+ console.debug("[WhitelabelService] Getting token for domain", {
177
+ hostname,
178
+ byEnv: process.env.WHITELABEL_DOMAIN,
179
+ });
180
+
181
+ const cache = this.createCache();
182
+ const missingCacheKey = this.getMissingCacheKey(hostname);
183
+
184
+ const cachedMissing = await cache.select(missingCacheKey);
185
+ if (cachedMissing.status == 1) {
186
+ console.log("[WhitelabelService] Cached miss for domain, using default", { hostname });
187
+ return this.getDefault();
188
+ }
189
+
190
+ const data = await this.lookup(hostname);
191
+ if (data) return data;
192
+
193
+ console.log("[WhitelabelService] No whitelabel for domain, falling back to default", {
194
+ hostname,
195
+ });
196
+ await cache.insert(missingCacheKey, "1", MISSING_WHITELABEL_CACHE_TTL_SECONDS);
197
+
198
+ return this.getDefault();
199
+ }
200
+
201
+ #matchesHostname(data: WhitelabelTokenData, hostname: string): boolean {
202
+ if (!data.domain) return true;
203
+
204
+ try {
205
+ return normalizeHostname(new URL(data.domain).hostname) === hostname;
206
+ } catch {
207
+ return true;
208
+ }
209
+ }
210
+
211
+ async getTokenByWhitelabelId(idWl: number): Promise<string> {
212
+ const apiUrl = this.getApiUrl();
213
+ const masterToken = this.getToken();
214
+ const url = `${apiUrl}/v1/pt-br/${idWl}/tokens?limit=1&page=1&sort=id:desc`;
215
+ console.debug("[WhitelabelService] Fetching token by whitelabel ID", {
216
+ url,
217
+ config: {
218
+ method: "GET",
219
+ headers: { authorization: masterToken },
220
+ },
221
+ });
222
+
223
+ const response = await fetch(url, {
224
+ method: "GET",
225
+ headers: { authorization: masterToken },
226
+ });
227
+
228
+ console.debug("[WhitelabelService] Response received for whitelabel ID", {
229
+ response,
230
+ });
231
+
232
+ if (!response.ok) {
233
+ throw new ApiError(
234
+ `Failed to fetch whitelabel token: ${response.status}`,
235
+ "WL_TOKEN_NOT_FOUND",
236
+ response.status,
237
+ );
238
+ }
239
+
240
+ const result: WhitelabelTokenApiResponse = await response.json();
241
+
242
+ if (result.status !== 1 || !result.data?.length) {
243
+ throw new ApiError(
244
+ "Token do whitelabel não encontrado",
245
+ "WL_TOKEN_NOT_FOUND",
246
+ 404,
247
+ );
248
+ }
249
+
250
+ return result.data[0].token;
251
+ }
252
+
253
+ async revalidateByDomain(hostname: string): Promise<WhitelabelTokenData> {
254
+ hostname = `${normalizeHostname(process.env.WHITELABEL_DOMAIN || hostname)}`;
255
+ console.debug("[WhitelabelService] Getting token for domain", {
256
+ hostname,
257
+ byEnv: process.env.WHITELABEL_DOMAIN,
258
+ });
259
+
260
+ const cache = this.createCache();
261
+ const cacheKey = this.getCacheKey(hostname);
262
+ const missingCacheKey = this.getMissingCacheKey(hostname);
263
+
264
+ console.log("[WhitelabelService] Revalidating cache for domain", {
265
+ hostname,
266
+ cacheKey,
267
+ });
268
+
269
+ await cache.delete(cacheKey);
270
+ await cache.delete(missingCacheKey);
271
+
272
+ const data = await this.getTokenByDomain(hostname);
273
+
274
+ console.log("[WhitelabelService] Cache revalidated for domain", {
275
+ hostname,
276
+ });
277
+
278
+ return data;
279
+ }
280
+ }
281
+
282
+ export const whitelabelService = new WhitelabelService();