@allanfsouza/aether-sdk 2.5.1 → 2.5.3

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/src/auth.ts CHANGED
@@ -1,8 +1,24 @@
1
1
  // src/auth.ts
2
+ // =============================================================================
3
+ // MÓDULO DE AUTENTICAÇÃO ADMINISTRATIVA - STANDALONE
4
+ // =============================================================================
5
+ // ⚠️ USO EXCLUSIVO DO WEB-ADMIN - NÃO EXPOSTO NO SDK PÚBLICO
6
+ //
7
+ // Este módulo gerencia autenticação contra a tabela GLOBAL 'users' do Aether
8
+ // (donos de projetos). Apps client-side devem usar TenantAuthModule.
9
+ //
10
+ // IMPORTANTE: Este módulo é STANDALONE - não depende do PlataformaClient.
11
+ // Ele pode ser importado diretamente:
12
+ // import { AuthModule } from "@allanfsouza/aether-sdk/dist/auth";
13
+ // =============================================================================
14
+
15
+ import axios from "axios";
2
16
  import type { AxiosInstance } from "axios";
3
- import type { PlataformaClient } from "./index.js";
4
17
 
5
- // Exportando a interface User para o App usar
18
+ // =============================================================================
19
+ // INTERFACES
20
+ // =============================================================================
21
+
6
22
  export interface User {
7
23
  id: string;
8
24
  name: string;
@@ -28,20 +44,74 @@ export interface Session {
28
44
  expiresAt: string;
29
45
  }
30
46
 
47
+ // =============================================================================
48
+ // CONSTANTES DE STORAGE
49
+ // Usamos chaves DIFERENTES do SDK público para evitar conflito
50
+ // =============================================================================
51
+ const ADMIN_STORAGE_KEYS = {
52
+ TOKEN: "aether_admin_token",
53
+ REFRESH_TOKEN: "aether_admin_refresh",
54
+ USER: "aether_admin_user",
55
+ } as const;
56
+
57
+ /**
58
+ * Verifica se estamos em ambiente browser com localStorage disponível.
59
+ */
60
+ function isBrowser(): boolean {
61
+ return (
62
+ typeof window !== "undefined" &&
63
+ typeof window.localStorage !== "undefined"
64
+ );
65
+ }
66
+
67
+ // =============================================================================
68
+ // CLASSE PRINCIPAL - STANDALONE
69
+ // =============================================================================
70
+
31
71
  export class AuthModule {
32
- private client: PlataformaClient;
33
72
  private http: AxiosInstance;
73
+ private apiUrl: string;
34
74
 
35
- // Armazena o user em memória para acesso rápido
36
- // Persistência fica no PlataformaClient (localStorage)
75
+ // Armazena dados em memória para acesso rápido
37
76
  private currentUser: User | null = null;
77
+ private accessToken: string | null = null;
78
+ private refreshToken: string | null = null;
38
79
 
39
- constructor(client: PlataformaClient, http: AxiosInstance) {
40
- this.client = client;
41
- this.http = http;
80
+ /**
81
+ * Cria uma instância do módulo de autenticação administrativa.
82
+ *
83
+ * @param apiUrl URL base da API do Aether (ex: https://api.aether.dev)
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import { AuthModule } from "@allanfsouza/aether-sdk/dist/auth";
88
+ *
89
+ * const auth = new AuthModule("https://api.aether.dev");
90
+ * await auth.login("admin@example.com", "password");
91
+ * ```
92
+ */
93
+ constructor(apiUrl: string) {
94
+ this.apiUrl = apiUrl.replace(/\/+$/, "");
95
+
96
+ // Cria instância HTTP dedicada para auth admin
97
+ this.http = axios.create({
98
+ baseURL: `${this.apiUrl}/v1`,
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ },
102
+ });
103
+
104
+ // Interceptor para adicionar token em todas as requisições
105
+ this.http.interceptors.request.use((config) => {
106
+ const token = this.getToken();
107
+ if (token) {
108
+ config.headers.Authorization = `Bearer ${token}`;
109
+ }
110
+ return config;
111
+ });
42
112
 
43
- // Restaura user do localStorage se existir
44
- this.currentUser = this.client.getUser();
113
+ // Restaura sessão do localStorage se existir
114
+ this._restoreSession();
45
115
  }
46
116
 
47
117
  // ==========================================================================
@@ -56,18 +126,13 @@ export class AuthModule {
56
126
  });
57
127
 
58
128
  if (data.accessToken) {
59
- // Persiste automaticamente via PlataformaClient
60
- this.client.setToken(data.accessToken);
61
- this.client.setRefreshToken(data.refreshToken);
62
- this.client.setUser(data.user);
63
- this.currentUser = data.user;
129
+ this._setSession(data.accessToken, data.refreshToken, data.user);
64
130
  }
65
131
 
66
132
  return data;
67
133
  } catch (e) {
68
134
  // Limpa tudo em caso de erro
69
- this.client.clearSession();
70
- this.currentUser = null;
135
+ this.clearSession();
71
136
  throw e;
72
137
  }
73
138
  }
@@ -76,27 +141,22 @@ export class AuthModule {
76
141
  name: string;
77
142
  email: string;
78
143
  password: string;
79
- }) {
80
- try {
81
- const { data } = await this.http.post("/auth/register", credentials);
82
-
83
- // Se o register retornar token (auto-login), configura sessão
84
- if (data.accessToken) {
85
- this.client.setToken(data.accessToken);
86
- this.client.setRefreshToken(data.refreshToken);
87
- this.client.setUser(data.user);
88
- this.currentUser = data.user;
89
- }
144
+ }): Promise<LoginResponse> {
145
+ const { data } = await this.http.post<LoginResponse>(
146
+ "/auth/register",
147
+ credentials
148
+ );
90
149
 
91
- return data;
92
- } catch (e) {
93
- throw e;
150
+ // Se o register retornar token (auto-login), configura sessão
151
+ if (data.accessToken) {
152
+ this._setSession(data.accessToken, data.refreshToken, data.user);
94
153
  }
154
+
155
+ return data;
95
156
  }
96
157
 
97
- async refresh(): Promise<{ accessToken: string }> {
98
- // Tenta pegar do localStorage primeiro, depois da memória
99
- const refreshToken = this.client.getRefreshToken() || this.getRefreshToken();
158
+ async refresh(): Promise<{ accessToken: string; refreshToken?: string }> {
159
+ const refreshToken = this.getRefreshToken();
100
160
 
101
161
  if (!refreshToken) {
102
162
  throw new Error("Nenhum refresh token disponível");
@@ -108,49 +168,48 @@ export class AuthModule {
108
168
  });
109
169
 
110
170
  if (data.accessToken) {
111
- this.client.setToken(data.accessToken);
171
+ this.accessToken = data.accessToken;
172
+ this._persistToken();
112
173
 
113
174
  // Se vier novo refresh token, atualiza
114
175
  if (data.refreshToken) {
115
- this.client.setRefreshToken(data.refreshToken);
176
+ this.refreshToken = data.refreshToken;
177
+ this._persistRefreshToken();
116
178
  }
117
179
  }
118
180
 
119
181
  return data;
120
182
  } catch (e) {
121
183
  // Token expirado - limpa sessão
122
- this.client.clearSession();
123
- this.currentUser = null;
184
+ this.clearSession();
124
185
  throw e;
125
186
  }
126
187
  }
127
188
 
128
189
  getGoogleAuthUrl(): string {
129
- return `${this.client.apiUrl}/v1/auth/google`;
190
+ return `${this.apiUrl}/v1/auth/google`;
130
191
  }
131
192
 
132
193
  async logout(): Promise<void> {
133
- const refreshToken = this.client.getRefreshToken();
194
+ const refreshToken = this.getRefreshToken();
134
195
 
135
196
  if (refreshToken) {
136
197
  try {
137
198
  await this.http.post("/auth/logout", { refreshToken });
138
199
  } catch {
139
- // Ignora erro de logout no servidor
200
+ // Ignora erro de logout no servidor - limpa local de qualquer forma
140
201
  }
141
202
  }
142
203
 
143
204
  // Limpa tudo (memória + localStorage)
144
- this.client.clearSession();
145
- this.currentUser = null;
205
+ this.clearSession();
146
206
  }
147
207
 
148
208
  async logoutAll(): Promise<void> {
149
209
  try {
150
210
  await this.http.post("/auth/logout-all");
151
211
  } finally {
152
- this.client.clearSession();
153
- this.currentUser = null;
212
+ this.clearSession();
154
213
  }
155
214
  }
156
215
 
@@ -161,12 +220,15 @@ export class AuthModule {
161
220
  return data.sessions;
162
221
  }
163
222
 
164
- async forgotPassword(email: string) {
223
+ async forgotPassword(email: string): Promise<{ message: string }> {
165
224
  const { data } = await this.http.post("/auth/forgot-password", { email });
166
225
  return data;
167
226
  }
168
227
 
169
- async resetPassword(token: string, newPassword: string) {
228
+ async resetPassword(
229
+ token: string,
230
+ newPassword: string
231
+ ): Promise<{ message: string }> {
170
232
  const { data } = await this.http.post("/auth/reset-password", {
171
233
  token,
172
234
  newPassword,
@@ -174,18 +236,34 @@ export class AuthModule {
174
236
  return data;
175
237
  }
176
238
 
239
+ // ==========================================================================
240
+ // GETTERS DE SESSÃO
241
+ // ==========================================================================
242
+
243
+ getToken(): string | null {
244
+ return this.accessToken;
245
+ }
246
+
247
+ getRefreshToken(): string | null {
248
+ return this.refreshToken;
249
+ }
250
+
251
+ getUser(): User | null {
252
+ return this.currentUser;
253
+ }
254
+
177
255
  /**
178
- * @deprecated Use client.getRefreshToken() - mantido para compatibilidade
256
+ * Alias para getUser() - compatibilidade com código existente.
179
257
  */
180
- getRefreshToken(): string | null {
181
- return this.client.getRefreshToken();
258
+ getCurrentUser(): User | null {
259
+ return this.currentUser;
182
260
  }
183
261
 
184
262
  /**
185
- * @deprecated Use client.setRefreshToken() - mantido para compatibilidade
263
+ * Verifica se existe uma sessão ativa.
186
264
  */
187
- setRefreshToken(token: string | null) {
188
- this.client.setRefreshToken(token);
265
+ isAuthenticated(): boolean {
266
+ return this.accessToken !== null && this.currentUser !== null;
189
267
  }
190
268
 
191
269
  // ==========================================================================
@@ -199,8 +277,9 @@ export class AuthModule {
199
277
  try {
200
278
  const data = await this.login(credentials.email, credentials.password);
201
279
  return { user: data.user, error: null };
202
- } catch (err: any) {
203
- return { user: null, error: err.response?.data?.message || err.message };
280
+ } catch (err: unknown) {
281
+ const error = err as { response?: { data?: { message?: string } }; message?: string };
282
+ return { user: null, error: error.response?.data?.message || error.message };
204
283
  }
205
284
  }
206
285
 
@@ -219,8 +298,9 @@ export class AuthModule {
219
298
  name: credentials.data?.name || credentials.email.split("@")[0],
220
299
  });
221
300
  return { user: data.user, error: null };
222
- } catch (err: any) {
223
- return { user: null, error: err.response?.data?.message || err.message };
301
+ } catch (err: unknown) {
302
+ const error = err as { response?: { data?: { message?: string } }; message?: string };
303
+ return { user: null, error: error.response?.data?.message || error.message };
224
304
  }
225
305
  }
226
306
 
@@ -237,8 +317,8 @@ export class AuthModule {
237
317
  * Primeiro tenta memória, depois localStorage.
238
318
  * Se não encontrar, retorna null.
239
319
  */
240
- async getSession() {
241
- const token = this.client.getToken();
320
+ async getSession(): Promise<{ user: User; access_token: string } | null> {
321
+ const token = this.getToken();
242
322
  if (!token) return null;
243
323
 
244
324
  // Tenta memória primeiro
@@ -249,38 +329,108 @@ export class AuthModule {
249
329
  };
250
330
  }
251
331
 
252
- // Tenta localStorage
253
- const savedUser = this.client.getUser();
254
- if (savedUser) {
255
- this.currentUser = savedUser;
256
- return {
257
- user: savedUser,
258
- access_token: token,
259
- };
260
- }
261
-
262
332
  // Última opção: valida token no backend
263
333
  try {
264
334
  const { data } = await this.http.get<{ user: User }>("/auth/me");
265
335
  this.currentUser = data.user;
266
- this.client.setUser(data.user);
336
+ this._persistUser();
267
337
  return {
268
338
  user: data.user,
269
339
  access_token: token,
270
340
  };
271
341
  } catch {
272
342
  // Token inválido/expirado - limpa sessão
273
- this.client.clearSession();
274
- this.currentUser = null;
343
+ this.clearSession();
275
344
  return null;
276
345
  }
277
346
  }
278
347
 
279
- /**
280
- * Retorna o usuário atual (sem validar token).
281
- * Útil para acesso síncrono aos dados do usuário.
282
- */
283
- getCurrentUser(): User | null {
284
- return this.currentUser || this.client.getUser();
348
+ // ==========================================================================
349
+ // GERENCIAMENTO DE SESSÃO (INTERNO)
350
+ // ==========================================================================
351
+
352
+ clearSession(): void {
353
+ this.accessToken = null;
354
+ this.refreshToken = null;
355
+ this.currentUser = null;
356
+
357
+ if (isBrowser()) {
358
+ localStorage.removeItem(ADMIN_STORAGE_KEYS.TOKEN);
359
+ localStorage.removeItem(ADMIN_STORAGE_KEYS.REFRESH_TOKEN);
360
+ localStorage.removeItem(ADMIN_STORAGE_KEYS.USER);
361
+ }
362
+ }
363
+
364
+ private _setSession(token: string, refresh: string, user: User): void {
365
+ this.accessToken = token;
366
+ this.refreshToken = refresh;
367
+ this.currentUser = user;
368
+ this._persistAll();
369
+ }
370
+
371
+ private _persistAll(): void {
372
+ this._persistToken();
373
+ this._persistRefreshToken();
374
+ this._persistUser();
375
+ }
376
+
377
+ private _persistToken(): void {
378
+ if (isBrowser() && this.accessToken) {
379
+ localStorage.setItem(ADMIN_STORAGE_KEYS.TOKEN, this.accessToken);
380
+ }
381
+ }
382
+
383
+ private _persistRefreshToken(): void {
384
+ if (isBrowser() && this.refreshToken) {
385
+ localStorage.setItem(ADMIN_STORAGE_KEYS.REFRESH_TOKEN, this.refreshToken);
386
+ }
387
+ }
388
+
389
+ private _persistUser(): void {
390
+ if (isBrowser() && this.currentUser) {
391
+ localStorage.setItem(
392
+ ADMIN_STORAGE_KEYS.USER,
393
+ JSON.stringify(this.currentUser)
394
+ );
395
+ }
396
+ }
397
+
398
+ private _restoreSession(): void {
399
+ if (!isBrowser()) return;
400
+
401
+ const token = localStorage.getItem(ADMIN_STORAGE_KEYS.TOKEN);
402
+ const refresh = localStorage.getItem(ADMIN_STORAGE_KEYS.REFRESH_TOKEN);
403
+ const userJson = localStorage.getItem(ADMIN_STORAGE_KEYS.USER);
404
+
405
+ if (token) this.accessToken = token;
406
+ if (refresh) this.refreshToken = refresh;
407
+
408
+ if (userJson) {
409
+ try {
410
+ this.currentUser = JSON.parse(userJson);
411
+ } catch {
412
+ // JSON inválido - limpa o dado corrompido
413
+ localStorage.removeItem(ADMIN_STORAGE_KEYS.USER);
414
+ }
415
+ }
285
416
  }
417
+ }
418
+
419
+ // =============================================================================
420
+ // FACTORY FUNCTION (Alternativa ao construtor)
421
+ // =============================================================================
422
+
423
+ /**
424
+ * Cria uma instância do módulo de autenticação administrativa.
425
+ *
426
+ * @example
427
+ * ```typescript
428
+ * import { createAdminAuth } from "@allanfsouza/aether-sdk/dist/auth";
429
+ *
430
+ * const auth = createAdminAuth("https://api.aether.dev");
431
+ * await auth.login("admin@example.com", "password");
432
+ * ```
433
+ */
434
+ export function createAdminAuth(apiUrl: string): AuthModule {
435
+ return new AuthModule(apiUrl);
286
436
  }
package/src/index.ts CHANGED
@@ -1,12 +1,25 @@
1
1
  // src/index.ts
2
+ // =============================================================================
3
+ // AETHER SDK - Cliente Principal
4
+ // =============================================================================
5
+ // SEGURANÇA: AuthModule foi REMOVIDO do SDK público.
6
+ // O módulo auth é exclusivo para o painel administrativo do Aether (web-admin).
7
+ // Apps client-side devem usar APENAS tenantAuth para autenticar seus usuários.
8
+ // =============================================================================
9
+
2
10
  import type { AxiosInstance } from "axios";
3
11
  import { createHttpClient } from "./http-client.js";
4
- import { AuthModule, User } from "./auth.js";
5
12
  import { DatabaseModule } from "./database.js";
6
13
  import { StorageModule } from "./storage.js";
7
14
  import { FunctionsModule } from "./functions.js";
8
15
  import { PushModule } from "./push.js";
9
- import { TenantAuthModule, TenantUser, TenantLoginResponse, TenantRegisterCredentials, TenantLoginCredentials } from "./tenant-auth.js";
16
+ import {
17
+ TenantAuthModule,
18
+ type TenantUser,
19
+ type TenantLoginResponse,
20
+ type TenantRegisterCredentials,
21
+ type TenantLoginCredentials,
22
+ } from "./tenant-auth.js";
10
23
  import { AIModule } from "./ai.js";
11
24
 
12
25
  // =============================================================================
@@ -30,7 +43,7 @@ export type ClientConfig = {
30
43
  apiKey?: string;
31
44
 
32
45
  /**
33
- * [NOVO] API Key de serviço para autenticação em operações de banco de dados.
46
+ * API Key de serviço para autenticação em operações de banco de dados.
34
47
  * Necessário para apps client-side que usam tenant auth.
35
48
  */
36
49
  serviceApiKey?: string;
@@ -54,7 +67,15 @@ function isBrowser(): boolean {
54
67
  }
55
68
 
56
69
  export class PlataformaClient {
57
- public auth: AuthModule;
70
+ // =========================================================================
71
+ // MÓDULOS PÚBLICOS
72
+ // =========================================================================
73
+ // NOTA DE SEGURANÇA: AuthModule foi REMOVIDO intencionalmente.
74
+ // Ele gerencia a tabela GLOBAL 'users' (donos de projetos Aether).
75
+ // Apps client-side NÃO devem ter acesso a essa funcionalidade.
76
+ // Use tenantAuth para autenticar usuários dos seus apps.
77
+ // =========================================================================
78
+
58
79
  public db: DatabaseModule;
59
80
  public storage: StorageModule;
60
81
  public functions: FunctionsModule;
@@ -96,8 +117,12 @@ export class PlataformaClient {
96
117
 
97
118
  this.http = createHttpClient(this);
98
119
 
99
- // Inicializa módulos
100
- this.auth = new AuthModule(this, this.http);
120
+ // =========================================================================
121
+ // INICIALIZAÇÃO DOS MÓDULOS
122
+ // =========================================================================
123
+ // SEGURANÇA: AuthModule NÃO é inicializado aqui.
124
+ // Apenas módulos seguros para client-side são expostos.
125
+ // =========================================================================
101
126
  this.db = new DatabaseModule(this, this.http);
102
127
  this.storage = new StorageModule(this, this.http);
103
128
  this.functions = new FunctionsModule(this, this.http);
@@ -142,7 +167,7 @@ export class PlataformaClient {
142
167
 
143
168
  /**
144
169
  * Salva o refresh token.
145
- * Usado internamente pelo AuthModule após login.
170
+ * Usado internamente pelo TenantAuthModule após login.
146
171
  */
147
172
  setRefreshToken(token: string | null): void {
148
173
  if (this._persistSession && isBrowser()) {
@@ -158,20 +183,21 @@ export class PlataformaClient {
158
183
  * Retorna o refresh token salvo no localStorage.
159
184
  */
160
185
  getRefreshToken(): string | null {
161
- if (this._persistSession && isBrowser()) {
186
+ if (isBrowser()) {
162
187
  return localStorage.getItem(STORAGE_KEYS.REFRESH_TOKEN);
163
188
  }
164
189
  return null;
165
190
  }
166
191
 
167
192
  // ===========================================================================
168
- // DADOS DO USUÁRIO
193
+ // USER DATA (TENANT USER)
169
194
  // ===========================================================================
170
195
 
171
196
  /**
172
- * Salva dados do usuário logado no localStorage.
197
+ * Salva dados do usuário tenant no localStorage.
198
+ * Usado internamente pelo TenantAuthModule após login.
173
199
  */
174
- setUser(user: User | null): void {
200
+ setUser(user: TenantUser | null): void {
175
201
  if (this._persistSession && isBrowser()) {
176
202
  if (user) {
177
203
  localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(user));
@@ -182,19 +208,18 @@ export class PlataformaClient {
182
208
  }
183
209
 
184
210
  /**
185
- * Retorna dados do usuário salvo no localStorage.
211
+ * Retorna dados do usuário tenant salvos no localStorage.
186
212
  */
187
- getUser(): User | null {
188
- if (this._persistSession && isBrowser()) {
189
- const saved = localStorage.getItem(STORAGE_KEYS.USER);
190
- if (saved) {
191
- try {
192
- return JSON.parse(saved) as User;
193
- } catch {
194
- // JSON corrompido - limpa
195
- localStorage.removeItem(STORAGE_KEYS.USER);
196
- return null;
213
+ getUser(): TenantUser | null {
214
+ if (isBrowser()) {
215
+ try {
216
+ const raw = localStorage.getItem(STORAGE_KEYS.USER);
217
+ if (raw) {
218
+ return JSON.parse(raw) as TenantUser;
197
219
  }
220
+ } catch {
221
+ // JSON inválido - limpa o dado corrompido
222
+ localStorage.removeItem(STORAGE_KEYS.USER);
198
223
  }
199
224
  }
200
225
  return null;
@@ -241,9 +266,14 @@ export class PlataformaClient {
241
266
  }
242
267
  }
243
268
 
244
- // ===== EXPORTS =====
269
+ // =============================================================================
270
+ // EXPORTS PÚBLICOS
271
+ // =============================================================================
272
+ // NOTA: Tipos do auth.ts (User, LoginResponse, Session) foram REMOVIDOS
273
+ // pois pertencem ao módulo administrativo, não ao SDK público.
274
+ // =============================================================================
275
+
245
276
  export { AetherError } from "./errors.js";
246
- export type { LoginResponse, Session, User } from "./auth.js";
247
277
  export type { ListOptions } from "./database.js";
248
278
  export type {
249
279
  PushPlatform,
package/tsconfig.json CHANGED
@@ -1,21 +1,29 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2020",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "lib": [
7
+ "ES2020",
8
+ "DOM"
9
+ ],
6
10
  "declaration": true,
11
+ "declarationMap": true,
12
+ "sourceMap": true,
7
13
  "outDir": "./dist",
8
14
  "rootDir": "./src",
9
15
  "strict": true,
16
+ "esModuleInterop": true,
10
17
  "skipLibCheck": true,
11
18
  "forceConsistentCasingInFileNames": true,
12
- // Adicionado DOM para reconhecer 'File', 'Blob' e 'WebSocket' nativos
13
- "lib": [
14
- "ES2020",
15
- "DOM"
16
- ]
19
+ "resolveJsonModule": true,
20
+ "allowSyntheticDefaultImports": true
17
21
  },
18
22
  "include": [
19
- "src"
23
+ "src/**/*.ts"
24
+ ],
25
+ "exclude": [
26
+ "node_modules",
27
+ "dist"
20
28
  ]
21
29
  }