@greatapps/common 1.1.795 → 1.1.797
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/modals/billing/BillingDataForm.mjs +406 -0
- package/dist/components/modals/billing/BillingDataForm.mjs.map +1 -0
- package/dist/components/modals/billing/RequiredBillingDataModal.mjs +5 -405
- package/dist/components/modals/billing/RequiredBillingDataModal.mjs.map +1 -1
- package/dist/i18n/resolve-locale.mjs +1 -1
- package/dist/i18n/resolve-locale.mjs.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/modules/ia-credits/types/ai-credits-offer.type.mjs +1 -0
- package/dist/modules/ia-credits/types/ai-credits-offer.type.mjs.map +1 -1
- package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs +1 -5
- package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
- package/dist/modules/whitelabel/constants/whitelabel.constants.mjs +9 -0
- package/dist/modules/whitelabel/constants/whitelabel.constants.mjs.map +1 -0
- package/dist/modules/whitelabel/services/whitelabel.service.mjs +85 -11
- package/dist/modules/whitelabel/services/whitelabel.service.mjs.map +1 -1
- package/dist/testing/msw/session.handlers.mjs +1 -0
- package/dist/testing/msw/session.handlers.mjs.map +1 -1
- package/package.json +10 -9
- package/src/components/modals/billing/BillingDataForm.tsx +489 -0
- package/src/components/modals/billing/RequiredBillingDataModal.tsx +3 -475
- package/src/i18n/resolve-locale.ts +1 -1
- package/src/index.ts +3 -0
- package/src/modules/ia-credits/types/ai-credits-offer.type.ts +1 -0
- package/src/modules/whitelabel/actions/find-whitelabel.action.ts +1 -7
- package/src/modules/whitelabel/constants/whitelabel.constants.ts +5 -0
- package/src/modules/whitelabel/services/whitelabel.service.ts +300 -209
- package/src/testing/msw/session.handlers.ts +1 -0
|
@@ -1,209 +1,300 @@
|
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (!data
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
1
|
+
import greatCache from "@greatapps/cache";
|
|
2
|
+
import { ApiError } from "../../../infra/api/types";
|
|
3
|
+
import { WhitelabelApiResponse, WhitelabelTokenApiResponse, WhitelabelTokenData } from "../schema";
|
|
4
|
+
import { normalizeHostname } from "../utils/normalize-hostname";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_WHITELABEL_ID,
|
|
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 getDefaultCacheKey(): string {
|
|
54
|
+
return `whitelabel-token-id-${DEFAULT_WHITELABEL_ID}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async fetchFromApi(hostname: string): Promise<WhitelabelTokenData | null> {
|
|
58
|
+
const apiUrl = this.getApiUrl();
|
|
59
|
+
const whitelabelMasterToken = this.getToken();
|
|
60
|
+
const url = `${apiUrl}/v1/pt-br/1/whitelabel/${hostname}/token`;
|
|
61
|
+
|
|
62
|
+
console.log("[WhitelabelService] Fetching token for domain", { url });
|
|
63
|
+
|
|
64
|
+
const response = await fetch(url, {
|
|
65
|
+
method: "GET",
|
|
66
|
+
headers: {
|
|
67
|
+
authorization: whitelabelMasterToken,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (response.status === 404) {
|
|
72
|
+
console.log("[WhitelabelService] No whitelabel for domain", {
|
|
73
|
+
hostname,
|
|
74
|
+
status: response.status,
|
|
75
|
+
});
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
console.error("[WhitelabelService] Failed to fetch whitelabel token", {
|
|
81
|
+
hostname,
|
|
82
|
+
status: response.status,
|
|
83
|
+
});
|
|
84
|
+
throw new ApiError(
|
|
85
|
+
`Failed to fetch whitelabel token: ${response.status}`,
|
|
86
|
+
"FETCH_ERROR",
|
|
87
|
+
response.status,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const result: WhitelabelTokenApiResponse = await response.json();
|
|
92
|
+
|
|
93
|
+
if (result.status !== 1 || !result.data?.length) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result.data[0];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async lookup(hostname: string): Promise<WhitelabelTokenData | null> {
|
|
101
|
+
const cache = this.createCache();
|
|
102
|
+
const cacheKey = this.getCacheKey(hostname);
|
|
103
|
+
|
|
104
|
+
const cachedData = await cache.select(cacheKey);
|
|
105
|
+
console.debug("[WhitelabelService] Cache lookup for domain", {
|
|
106
|
+
cacheKey,
|
|
107
|
+
cachedData,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (cachedData.status == 1 && "data" in cachedData && cachedData.data) {
|
|
111
|
+
const cachedWhitelabel: WhitelabelTokenData = JSON.parse(cachedData.data);
|
|
112
|
+
|
|
113
|
+
if (this.#matchesHostname(cachedWhitelabel, hostname)) {
|
|
114
|
+
console.log("[WhitelabelService] Cache hit for domain", { hostname });
|
|
115
|
+
return cachedWhitelabel;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.error("[WhitelabelService] Cached whitelabel does not match hostname", {
|
|
119
|
+
hostname,
|
|
120
|
+
cacheKey,
|
|
121
|
+
whitelabelId: cachedWhitelabel.id,
|
|
122
|
+
whitelabelDomain: cachedWhitelabel.domain,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await cache.delete(cacheKey);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const data = await this.fetchFromApi(hostname);
|
|
129
|
+
if (!data) return null;
|
|
130
|
+
|
|
131
|
+
if (!this.#matchesHostname(data, hostname)) {
|
|
132
|
+
console.error("[WhitelabelService] API returned whitelabel of another hostname", {
|
|
133
|
+
hostname,
|
|
134
|
+
whitelabelId: data.id,
|
|
135
|
+
whitelabelDomain: data.domain,
|
|
136
|
+
});
|
|
137
|
+
return data;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await cache.insert(cacheKey, JSON.stringify(data), WHITELABEL_CACHE_TTL_SECONDS);
|
|
141
|
+
|
|
142
|
+
return data;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async getDefault(): Promise<WhitelabelTokenData> {
|
|
146
|
+
const cache = this.createCache();
|
|
147
|
+
const cacheKey = this.getDefaultCacheKey();
|
|
148
|
+
|
|
149
|
+
const cachedData = await cache.select(cacheKey);
|
|
150
|
+
if (cachedData.status == 1 && "data" in cachedData && cachedData.data) {
|
|
151
|
+
const cachedWhitelabel: WhitelabelTokenData = JSON.parse(cachedData.data);
|
|
152
|
+
return cachedWhitelabel;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const apiUrl = this.getApiUrl();
|
|
156
|
+
const masterToken = this.getToken();
|
|
157
|
+
const url = `${apiUrl}/v1/pt-br/${DEFAULT_WHITELABEL_ID}/whitelabels/${DEFAULT_WHITELABEL_ID}`;
|
|
158
|
+
|
|
159
|
+
const response = await fetch(url, {
|
|
160
|
+
method: "GET",
|
|
161
|
+
headers: {
|
|
162
|
+
authorization: masterToken,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
throw new ApiError(
|
|
168
|
+
`Default whitelabel not found: ${DEFAULT_WHITELABEL_ID}`,
|
|
169
|
+
"WL_DEFAULT_UNAVAILABLE",
|
|
170
|
+
500,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const result: WhitelabelApiResponse = await response.json();
|
|
175
|
+
|
|
176
|
+
if (result.status !== 1 || !result.data?.length) {
|
|
177
|
+
throw new ApiError(
|
|
178
|
+
`Default whitelabel not found: ${DEFAULT_WHITELABEL_ID}`,
|
|
179
|
+
"WL_DEFAULT_UNAVAILABLE",
|
|
180
|
+
500,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const data: WhitelabelTokenData = { ...result.data[0], token: masterToken };
|
|
185
|
+
|
|
186
|
+
await cache.insert(cacheKey, JSON.stringify(data), WHITELABEL_CACHE_TTL_SECONDS);
|
|
187
|
+
|
|
188
|
+
return data;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async getTokenByDomain(hostname: string): Promise<WhitelabelTokenData> {
|
|
192
|
+
hostname = `${normalizeHostname(process.env.WHITELABEL_DOMAIN || hostname)}`;
|
|
193
|
+
console.debug("[WhitelabelService] Getting token for domain", {
|
|
194
|
+
hostname,
|
|
195
|
+
byEnv: process.env.WHITELABEL_DOMAIN,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const cache = this.createCache();
|
|
199
|
+
const missingCacheKey = this.getMissingCacheKey(hostname);
|
|
200
|
+
|
|
201
|
+
const cachedMissing = await cache.select(missingCacheKey);
|
|
202
|
+
if (cachedMissing.status == 1) {
|
|
203
|
+
console.log("[WhitelabelService] Cached miss for domain, using default", { hostname });
|
|
204
|
+
return this.getDefault();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const data = await this.lookup(hostname);
|
|
208
|
+
if (data) return data;
|
|
209
|
+
|
|
210
|
+
console.log("[WhitelabelService] No whitelabel for domain, falling back to default", {
|
|
211
|
+
hostname,
|
|
212
|
+
});
|
|
213
|
+
await cache.insert(missingCacheKey, "1", MISSING_WHITELABEL_CACHE_TTL_SECONDS);
|
|
214
|
+
|
|
215
|
+
return this.getDefault();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#matchesHostname(data: WhitelabelTokenData, hostname: string): boolean {
|
|
219
|
+
if (!data.domain) return true;
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
return normalizeHostname(new URL(data.domain).hostname) === hostname;
|
|
223
|
+
} catch {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async getTokenByWhitelabelId(idWl: number): Promise<string> {
|
|
229
|
+
const apiUrl = this.getApiUrl();
|
|
230
|
+
const masterToken = this.getToken();
|
|
231
|
+
const url = `${apiUrl}/v1/pt-br/${idWl}/tokens?limit=1&page=1&sort=id:desc`;
|
|
232
|
+
console.debug("[WhitelabelService] Fetching token by whitelabel ID", {
|
|
233
|
+
url,
|
|
234
|
+
config: {
|
|
235
|
+
method: "GET",
|
|
236
|
+
headers: { authorization: masterToken },
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const response = await fetch(url, {
|
|
241
|
+
method: "GET",
|
|
242
|
+
headers: { authorization: masterToken },
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
console.debug("[WhitelabelService] Response received for whitelabel ID", {
|
|
246
|
+
response,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
throw new ApiError(
|
|
251
|
+
`Failed to fetch whitelabel token: ${response.status}`,
|
|
252
|
+
"WL_TOKEN_NOT_FOUND",
|
|
253
|
+
response.status,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const result: WhitelabelTokenApiResponse = await response.json();
|
|
258
|
+
|
|
259
|
+
if (result.status !== 1 || !result.data?.length) {
|
|
260
|
+
throw new ApiError(
|
|
261
|
+
"Token do whitelabel não encontrado",
|
|
262
|
+
"WL_TOKEN_NOT_FOUND",
|
|
263
|
+
404,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return result.data[0].token;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async revalidateByDomain(hostname: string): Promise<WhitelabelTokenData> {
|
|
271
|
+
hostname = `${normalizeHostname(process.env.WHITELABEL_DOMAIN || hostname)}`;
|
|
272
|
+
console.debug("[WhitelabelService] Getting token for domain", {
|
|
273
|
+
hostname,
|
|
274
|
+
byEnv: process.env.WHITELABEL_DOMAIN,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const cache = this.createCache();
|
|
278
|
+
const cacheKey = this.getCacheKey(hostname);
|
|
279
|
+
const missingCacheKey = this.getMissingCacheKey(hostname);
|
|
280
|
+
|
|
281
|
+
console.log("[WhitelabelService] Revalidating cache for domain", {
|
|
282
|
+
hostname,
|
|
283
|
+
cacheKey,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await cache.delete(cacheKey);
|
|
287
|
+
await cache.delete(missingCacheKey);
|
|
288
|
+
await cache.delete(this.getDefaultCacheKey());
|
|
289
|
+
|
|
290
|
+
const data = await this.getTokenByDomain(hostname);
|
|
291
|
+
|
|
292
|
+
console.log("[WhitelabelService] Cache revalidated for domain", {
|
|
293
|
+
hostname,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return data;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const whitelabelService = new WhitelabelService();
|
|
@@ -26,6 +26,7 @@ export function makeSessionHandlers(opts: SessionHandlersOptions = {}) {
|
|
|
26
26
|
|
|
27
27
|
return [
|
|
28
28
|
http.get(`${base}/v1/:locale/:wl/whitelabel/:host/token`, () => apiFind(whitelabel)),
|
|
29
|
+
http.get(`${base}/v1/:locale/:wl/whitelabels/:id`, () => apiFind(whitelabel)),
|
|
29
30
|
http.get(`${base}/v1/:locale/:wl/tokens`, () => apiFind(whitelabel)),
|
|
30
31
|
// The users path has more segments, so it must come before /accounts/:acc.
|
|
31
32
|
http.get(`${base}/v1/:locale/:wl/accounts/:acc/users/:user`, () => apiFind(user)),
|