@allanfsouza/aether-sdk 2.1.0 → 2.2.0

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/auth.d.ts CHANGED
@@ -1,16 +1,79 @@
1
1
  import type { AxiosInstance } from "axios";
2
2
  import type { PlataformaClient } from "./index.js";
3
+ export interface LoginResponse {
4
+ accessToken: string;
5
+ refreshToken: string;
6
+ user: {
7
+ id: string;
8
+ name: string;
9
+ email: string;
10
+ avatarUrl?: string;
11
+ role?: string;
12
+ aetherRole?: string;
13
+ planCode?: string;
14
+ emailVerified?: boolean;
15
+ };
16
+ }
17
+ export interface Session {
18
+ id: string;
19
+ userAgent?: string;
20
+ ip?: string;
21
+ createdAt: string;
22
+ expiresAt: string;
23
+ }
3
24
  export declare class AuthModule {
4
25
  private client;
5
26
  private http;
27
+ private refreshToken;
6
28
  constructor(client: PlataformaClient, http: AxiosInstance);
7
- login(email: string, password: string): Promise<any>;
29
+ /**
30
+ * Login com email e senha
31
+ */
32
+ login(email: string, password: string): Promise<LoginResponse>;
33
+ /**
34
+ * Registrar novo usuário
35
+ */
8
36
  register(credentials: {
9
37
  name: string;
10
38
  email: string;
11
39
  password: string;
12
40
  }): Promise<any>;
41
+ /**
42
+ * Renovar access token usando refresh token
43
+ */
44
+ refresh(): Promise<{
45
+ accessToken: string;
46
+ }>;
47
+ /**
48
+ * Obter URL de autenticação do Google
49
+ */
50
+ getGoogleAuthUrl(): string;
51
+ /**
52
+ * Logout da sessão atual
53
+ */
54
+ logout(): Promise<void>;
55
+ /**
56
+ * Logout de todas as sessões
57
+ */
58
+ logoutAll(): Promise<void>;
59
+ /**
60
+ * Listar sessões ativas
61
+ */
62
+ getSessions(): Promise<Session[]>;
63
+ /**
64
+ * Esqueci minha senha
65
+ */
13
66
  forgotPassword(email: string): Promise<any>;
67
+ /**
68
+ * Redefinir senha
69
+ */
14
70
  resetPassword(token: string, newPassword: string): Promise<any>;
15
- logout(): void;
71
+ /**
72
+ * Obter refresh token atual (para armazenamento)
73
+ */
74
+ getRefreshToken(): string | null;
75
+ /**
76
+ * Definir refresh token (para restaurar sessão)
77
+ */
78
+ setRefreshToken(token: string | null): void;
16
79
  }
package/dist/auth.js CHANGED
@@ -1,43 +1,133 @@
1
1
  export class AuthModule {
2
2
  constructor(client, http) {
3
+ this.refreshToken = null;
3
4
  this.client = client;
4
5
  this.http = http;
5
6
  }
7
+ /**
8
+ * Login com email e senha
9
+ */
6
10
  async login(email, password) {
7
11
  try {
8
- const { data } = await this.http.post("/auth/login", { email, password });
9
- if (data.token) {
10
- this.client.setToken(data.token);
12
+ const { data } = await this.http.post("/auth/login", {
13
+ email,
14
+ password,
15
+ });
16
+ if (data.accessToken) {
17
+ this.client.setToken(data.accessToken);
18
+ this.refreshToken = data.refreshToken;
11
19
  }
12
20
  return data;
13
21
  }
14
22
  catch (e) {
15
23
  this.client.setToken(null);
24
+ this.refreshToken = null;
16
25
  throw e;
17
26
  }
18
27
  }
28
+ /**
29
+ * Registrar novo usuário
30
+ */
19
31
  async register(credentials) {
20
32
  try {
21
33
  const { data } = await this.http.post("/auth/register", credentials);
22
- if (data.token) {
23
- this.client.setToken(data.token);
34
+ return data;
35
+ }
36
+ catch (e) {
37
+ throw e;
38
+ }
39
+ }
40
+ /**
41
+ * Renovar access token usando refresh token
42
+ */
43
+ async refresh() {
44
+ if (!this.refreshToken) {
45
+ throw new Error("Nenhum refresh token disponível");
46
+ }
47
+ try {
48
+ const { data } = await this.http.post("/auth/refresh", {
49
+ refreshToken: this.refreshToken,
50
+ });
51
+ if (data.accessToken) {
52
+ this.client.setToken(data.accessToken);
24
53
  }
25
54
  return data;
26
55
  }
27
56
  catch (e) {
28
57
  this.client.setToken(null);
58
+ this.refreshToken = null;
29
59
  throw e;
30
60
  }
31
61
  }
62
+ /**
63
+ * Obter URL de autenticação do Google
64
+ */
65
+ getGoogleAuthUrl() {
66
+ return `${this.client.apiUrl}/v1/auth/google`;
67
+ }
68
+ /**
69
+ * Logout da sessão atual
70
+ */
71
+ async logout() {
72
+ if (this.refreshToken) {
73
+ try {
74
+ await this.http.post("/auth/logout", {
75
+ refreshToken: this.refreshToken,
76
+ });
77
+ }
78
+ catch (e) {
79
+ // Ignora erro de logout
80
+ }
81
+ }
82
+ this.client.setToken(null);
83
+ this.refreshToken = null;
84
+ }
85
+ /**
86
+ * Logout de todas as sessões
87
+ */
88
+ async logoutAll() {
89
+ try {
90
+ await this.http.post("/auth/logout-all");
91
+ }
92
+ finally {
93
+ this.client.setToken(null);
94
+ this.refreshToken = null;
95
+ }
96
+ }
97
+ /**
98
+ * Listar sessões ativas
99
+ */
100
+ async getSessions() {
101
+ const { data } = await this.http.get("/auth/sessions");
102
+ return data.sessions;
103
+ }
104
+ /**
105
+ * Esqueci minha senha
106
+ */
32
107
  async forgotPassword(email) {
33
108
  const { data } = await this.http.post("/auth/forgot-password", { email });
34
109
  return data;
35
110
  }
111
+ /**
112
+ * Redefinir senha
113
+ */
36
114
  async resetPassword(token, newPassword) {
37
- const { data } = await this.http.post("/auth/reset-password", { token, newPassword });
115
+ const { data } = await this.http.post("/auth/reset-password", {
116
+ token,
117
+ newPassword,
118
+ });
38
119
  return data;
39
120
  }
40
- logout() {
41
- this.client.setToken(null);
121
+ /**
122
+ * Obter refresh token atual (para armazenamento)
123
+ */
124
+ getRefreshToken() {
125
+ return this.refreshToken;
126
+ }
127
+ /**
128
+ * Definir refresh token (para restaurar sessão)
129
+ */
130
+ setRefreshToken(token) {
131
+ this.refreshToken = token;
42
132
  }
43
133
  }
@@ -1,11 +1,85 @@
1
1
  import type { AxiosInstance } from "axios";
2
2
  import type { PlataformaClient } from "./index.js";
3
3
  export type ListOptions<T> = {
4
- filter?: Partial<T>;
4
+ filter?: Partial<T> | Record<string, any>;
5
5
  sort?: {
6
6
  field: keyof T & string;
7
7
  order: "ASC" | "DESC";
8
8
  };
9
+ limit?: number;
10
+ offset?: number;
11
+ };
12
+ /**
13
+ * Builder para construção fluente de queries.
14
+ */
15
+ export declare class QueryBuilder<T> {
16
+ private collectionRef;
17
+ private filter;
18
+ private sort?;
19
+ private limitVal?;
20
+ private offsetVal?;
21
+ constructor(collectionRef: CollectionReference<T>);
22
+ /**
23
+ * Adiciona um filtro de igualdade.
24
+ */
25
+ eq(column: keyof T & string, value: any): this;
26
+ /**
27
+ * Adiciona um filtro de desigualdade ($ne).
28
+ */
29
+ neq(column: keyof T & string, value: any): this;
30
+ /**
31
+ * Adiciona um filtro maior que ($gt).
32
+ */
33
+ gt(column: keyof T & string, value: number | string): this;
34
+ /**
35
+ * Adiciona um filtro maior ou igual ($gte).
36
+ */
37
+ gte(column: keyof T & string, value: number | string): this;
38
+ /**
39
+ * Adiciona um filtro menor que ($lt).
40
+ */
41
+ lt(column: keyof T & string, value: number | string): this;
42
+ /**
43
+ * Adiciona um filtro menor ou igual ($lte).
44
+ */
45
+ lte(column: keyof T & string, value: number | string): this;
46
+ /**
47
+ * Adiciona um filtro LIKE ($like).
48
+ */
49
+ like(column: keyof T & string, value: string): this;
50
+ /**
51
+ * Define a ordenação.
52
+ */
53
+ order(column: keyof T & string, direction?: "ASC" | "DESC"): this;
54
+ /**
55
+ * Define o limite de registros.
56
+ */
57
+ limit(count: number): this;
58
+ /**
59
+ * Define o deslocamento (paginação).
60
+ */
61
+ offset(count: number): this;
62
+ /**
63
+ * Executa a query e retorna os resultados.
64
+ */
65
+ get(): Promise<T[]>;
66
+ }
67
+ /**
68
+ * Operação em lote.
69
+ */
70
+ export type BatchOperation = {
71
+ type: "create";
72
+ collection: string;
73
+ data: any;
74
+ } | {
75
+ type: "update";
76
+ collection: string;
77
+ id: string;
78
+ data: any;
79
+ } | {
80
+ type: "delete";
81
+ collection: string;
82
+ id: string;
9
83
  };
10
84
  /**
11
85
  * Módulo de Banco de Dados
@@ -20,17 +94,36 @@ export declare class DatabaseModule {
20
94
  * * @example client.db.collection<Product>('products')
21
95
  */
22
96
  collection<T = any>(collectionName: string): CollectionReference<T>;
97
+ /**
98
+ * Executa múltiplas operações em uma única transação.
99
+ * Se uma falhar, todas são revertidas.
100
+ */
101
+ batch(operations: BatchOperation[]): Promise<any[]>;
23
102
  }
24
103
  /**
25
104
  * Referência a uma coleção específica.
26
105
  * O <T> define o formato dos dados (ex: interface User).
27
106
  */
28
- declare class CollectionReference<T> {
107
+ export declare class CollectionReference<T> {
29
108
  private client;
30
109
  private http;
31
110
  private collectionName;
32
111
  private wsUrl;
33
112
  constructor(client: PlataformaClient, http: AxiosInstance, name: string);
113
+ /**
114
+ * Inicia o QueryBuilder.
115
+ * Atalho para .eq()
116
+ */
117
+ eq(column: keyof T & string, value: any): QueryBuilder<T>;
118
+ /**
119
+ * Inicia o QueryBuilder.
120
+ * Atalho para .gt()
121
+ */
122
+ gt(column: keyof T & string, value: number | string): QueryBuilder<T>;
123
+ /**
124
+ * Retorna uma nova instância do QueryBuilder.
125
+ */
126
+ query(): QueryBuilder<T>;
34
127
  /**
35
128
  * Lista documentos da coleção com filtros opcionais.
36
129
  * @param options Filtros e Ordenação
@@ -59,4 +152,3 @@ declare class CollectionReference<T> {
59
152
  */
60
153
  subscribe(callback: (action: "create" | "update" | "delete", data: T) => void): () => void;
61
154
  }
62
- export {};
package/dist/database.js CHANGED
@@ -1,4 +1,94 @@
1
1
  import WebSocket from "ws";
2
+ /**
3
+ * Builder para construção fluente de queries.
4
+ */
5
+ export class QueryBuilder {
6
+ constructor(collectionRef) {
7
+ this.filter = {};
8
+ this.collectionRef = collectionRef;
9
+ }
10
+ /**
11
+ * Adiciona um filtro de igualdade.
12
+ */
13
+ eq(column, value) {
14
+ this.filter[column] = value;
15
+ return this;
16
+ }
17
+ /**
18
+ * Adiciona um filtro de desigualdade ($ne).
19
+ */
20
+ neq(column, value) {
21
+ this.filter[column] = { ...this.filter[column], $ne: value };
22
+ return this;
23
+ }
24
+ /**
25
+ * Adiciona um filtro maior que ($gt).
26
+ */
27
+ gt(column, value) {
28
+ this.filter[column] = { ...this.filter[column], $gt: value };
29
+ return this;
30
+ }
31
+ /**
32
+ * Adiciona um filtro maior ou igual ($gte).
33
+ */
34
+ gte(column, value) {
35
+ this.filter[column] = { ...this.filter[column], $gte: value };
36
+ return this;
37
+ }
38
+ /**
39
+ * Adiciona um filtro menor que ($lt).
40
+ */
41
+ lt(column, value) {
42
+ this.filter[column] = { ...this.filter[column], $lt: value };
43
+ return this;
44
+ }
45
+ /**
46
+ * Adiciona um filtro menor ou igual ($lte).
47
+ */
48
+ lte(column, value) {
49
+ this.filter[column] = { ...this.filter[column], $lte: value };
50
+ return this;
51
+ }
52
+ /**
53
+ * Adiciona um filtro LIKE ($like).
54
+ */
55
+ like(column, value) {
56
+ this.filter[column] = { ...this.filter[column], $like: value };
57
+ return this;
58
+ }
59
+ /**
60
+ * Define a ordenação.
61
+ */
62
+ order(column, direction = "ASC") {
63
+ this.sort = { field: column, order: direction };
64
+ return this;
65
+ }
66
+ /**
67
+ * Define o limite de registros.
68
+ */
69
+ limit(count) {
70
+ this.limitVal = count;
71
+ return this;
72
+ }
73
+ /**
74
+ * Define o deslocamento (paginação).
75
+ */
76
+ offset(count) {
77
+ this.offsetVal = count;
78
+ return this;
79
+ }
80
+ /**
81
+ * Executa a query e retorna os resultados.
82
+ */
83
+ async get() {
84
+ return this.collectionRef.list({
85
+ filter: this.filter,
86
+ sort: this.sort,
87
+ limit: this.limitVal,
88
+ offset: this.offsetVal,
89
+ });
90
+ }
91
+ }
2
92
  /**
3
93
  * Módulo de Banco de Dados
4
94
  */
@@ -15,12 +105,20 @@ export class DatabaseModule {
15
105
  collection(collectionName) {
16
106
  return new CollectionReference(this.client, this.http, collectionName);
17
107
  }
108
+ /**
109
+ * Executa múltiplas operações em uma única transação.
110
+ * Se uma falhar, todas são revertidas.
111
+ */
112
+ async batch(operations) {
113
+ const { data } = await this.http.post("/db/batch", { operations });
114
+ return data.results;
115
+ }
18
116
  }
19
117
  /**
20
118
  * Referência a uma coleção específica.
21
119
  * O <T> define o formato dos dados (ex: interface User).
22
120
  */
23
- class CollectionReference {
121
+ export class CollectionReference {
24
122
  constructor(client, http, name) {
25
123
  this.client = client;
26
124
  this.http = http;
@@ -29,6 +127,27 @@ class CollectionReference {
29
127
  const protocol = client.apiUrl.startsWith("https") ? "wss" : "ws";
30
128
  this.wsUrl = client.apiUrl.replace(/^https?/, protocol);
31
129
  }
130
+ /**
131
+ * Inicia o QueryBuilder.
132
+ * Atalho para .eq()
133
+ */
134
+ eq(column, value) {
135
+ return new QueryBuilder(this).eq(column, value);
136
+ }
137
+ /**
138
+ * Inicia o QueryBuilder.
139
+ * Atalho para .gt()
140
+ */
141
+ gt(column, value) {
142
+ return new QueryBuilder(this).gt(column, value);
143
+ }
144
+ // ... Outros atalhos podem ser adicionados conforme necessidade ...
145
+ /**
146
+ * Retorna uma nova instância do QueryBuilder.
147
+ */
148
+ query() {
149
+ return new QueryBuilder(this);
150
+ }
32
151
  /**
33
152
  * Lista documentos da coleção com filtros opcionais.
34
153
  * @param options Filtros e Ordenação
@@ -43,6 +162,9 @@ class CollectionReference {
43
162
  // Backend espera formato array: ["campo", "DESC"]
44
163
  params.sort = JSON.stringify([options.sort.field, options.sort.order]);
45
164
  }
165
+ // TODO: Backend precisa implementar limit/offset na rota GET
166
+ // if (options?.limit) params.limit = options.limit;
167
+ // if (options?.offset) params.offset = options.offset;
46
168
  const { data } = await this.http.get(`/db/${this.collectionName}`, {
47
169
  params,
48
170
  });
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import type { AxiosInstance } from "axios";
1
2
  import { AuthModule } from "./auth.js";
2
3
  import { DatabaseModule } from "./database.js";
3
4
  import { StorageModule } from "./storage.js";
4
5
  import { FunctionsModule } from "./functions.js";
6
+ import { PushModule } from "./push.js";
5
7
  /**
6
8
  * Configuração usada para criar o cliente principal da plataforma.
7
9
  */
@@ -11,16 +13,17 @@ export type ClientConfig = {
11
13
  };
12
14
  /**
13
15
  * O cliente principal da Plataforma API (Aether).
14
- * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions).
16
+ * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions, Push).
15
17
  */
16
18
  export declare class PlataformaClient {
17
19
  auth: AuthModule;
18
20
  db: DatabaseModule;
19
21
  storage: StorageModule;
20
22
  functions: FunctionsModule;
23
+ push: PushModule;
21
24
  apiUrl: string;
22
25
  projectId: string;
23
- private http;
26
+ http: AxiosInstance;
24
27
  private _token;
25
28
  constructor(config: ClientConfig);
26
29
  /**
@@ -36,3 +39,4 @@ export declare class PlataformaClient {
36
39
  }
37
40
  export { AetherError } from "./errors.js";
38
41
  export type { ListOptions } from "./database.js";
42
+ export type { PushPlatform, PushEnvironment, PushDevice, RegisterDeviceParams, SendPushResponse, PushStatus, PushLogEntry, ListPushLogsOptions, PushStats, } from "./push.js";
package/dist/index.js CHANGED
@@ -3,9 +3,10 @@ import { AuthModule } from "./auth.js";
3
3
  import { DatabaseModule } from "./database.js";
4
4
  import { StorageModule } from "./storage.js";
5
5
  import { FunctionsModule } from "./functions.js";
6
+ import { PushModule } from "./push.js";
6
7
  /**
7
8
  * O cliente principal da Plataforma API (Aether).
8
- * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions).
9
+ * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions, Push).
9
10
  */
10
11
  export class PlataformaClient {
11
12
  constructor(config) {
@@ -13,8 +14,8 @@ export class PlataformaClient {
13
14
  if (!config.apiUrl || !config.projectId) {
14
15
  throw new Error("apiUrl e projectId são obrigatórios.");
15
16
  }
16
- // Normaliza apiUrl removendo barra final, se houver
17
- this.apiUrl = config.apiUrl.replace(/\/$/, "");
17
+ // Normaliza apiUrl removendo barras finais, se houver
18
+ this.apiUrl = config.apiUrl.replace(/\/+$/, "");
18
19
  this.projectId = config.projectId;
19
20
  // Inicializa o cliente HTTP (passando a própria instância)
20
21
  this.http = createHttpClient(this);
@@ -23,6 +24,7 @@ export class PlataformaClient {
23
24
  this.db = new DatabaseModule(this, this.http);
24
25
  this.storage = new StorageModule(this, this.http);
25
26
  this.functions = new FunctionsModule(this, this.http);
27
+ this.push = new PushModule(this, this.http);
26
28
  }
27
29
  // --- Gerenciamento de Token ---
28
30
  /**
package/dist/push.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { PlataformaClient } from "./index.js";
3
+ export type PushPlatform = "android" | "ios" | "web";
4
+ export type PushEnvironment = "dev" | "staging" | "prod";
5
+ export interface RegisterDeviceParams {
6
+ platform: PushPlatform;
7
+ token: string;
8
+ environment?: PushEnvironment;
9
+ }
10
+ export interface PushDevice {
11
+ id: string;
12
+ projectId: string;
13
+ userId?: string | null;
14
+ platform: PushPlatform;
15
+ token: string;
16
+ environment: PushEnvironment;
17
+ createdAt: string;
18
+ lastSeenAt: string;
19
+ }
20
+ export interface RegisterDeviceResponse {
21
+ device: PushDevice;
22
+ }
23
+ export interface SendPushResponse {
24
+ sent: number;
25
+ total: number;
26
+ message: string;
27
+ }
28
+ export type PushStatus = "sent" | "failed";
29
+ export interface PushLogEntry {
30
+ id: string;
31
+ projectId: string;
32
+ userId?: string | null;
33
+ deviceId?: string | null;
34
+ title: string;
35
+ body: string;
36
+ data: Record<string, string>;
37
+ status: PushStatus;
38
+ errorMessage?: string | null;
39
+ createdAt: string;
40
+ }
41
+ export interface ListPushLogsOptions {
42
+ limit?: number;
43
+ offset?: number;
44
+ status?: PushStatus;
45
+ }
46
+ export interface PushStats {
47
+ totalDevices: number;
48
+ sent24h: number;
49
+ successRate: number;
50
+ }
51
+ export declare class PushModule {
52
+ private client;
53
+ private http;
54
+ constructor(client: PlataformaClient, http: AxiosInstance);
55
+ registerDevice(params: RegisterDeviceParams): Promise<PushDevice>;
56
+ sendToToken(params: {
57
+ token: string;
58
+ title: string;
59
+ body: string;
60
+ data?: Record<string, string>;
61
+ environment?: PushEnvironment;
62
+ }): Promise<SendPushResponse>;
63
+ sendToUser(params: {
64
+ userId: string;
65
+ title: string;
66
+ body: string;
67
+ data?: Record<string, string>;
68
+ environment?: PushEnvironment;
69
+ }): Promise<SendPushResponse>;
70
+ listLogs(options?: ListPushLogsOptions): Promise<PushLogEntry[]>;
71
+ getStats(): Promise<PushStats>;
72
+ }
package/dist/push.js ADDED
@@ -0,0 +1,53 @@
1
+ export class PushModule {
2
+ constructor(client, http) {
3
+ this.client = client;
4
+ this.http = http;
5
+ }
6
+ async registerDevice(params) {
7
+ const projectId = this.client.projectId;
8
+ const { data } = await this.http.post(`/projects/${projectId}/push/devices`, {
9
+ platform: params.platform,
10
+ token: params.token,
11
+ environment: params.environment ?? "prod",
12
+ });
13
+ return data.device;
14
+ }
15
+ async sendToToken(params) {
16
+ const projectId = this.client.projectId;
17
+ const { data } = await this.http.post(`/projects/${projectId}/push/send`, {
18
+ token: params.token,
19
+ title: params.title,
20
+ body: params.body,
21
+ data: params.data ?? {},
22
+ environment: params.environment ?? "prod",
23
+ });
24
+ return data;
25
+ }
26
+ async sendToUser(params) {
27
+ const projectId = this.client.projectId;
28
+ const { data } = await this.http.post(`/projects/${projectId}/push/send`, {
29
+ userId: params.userId,
30
+ title: params.title,
31
+ body: params.body,
32
+ data: params.data ?? {},
33
+ environment: params.environment ?? "prod",
34
+ });
35
+ return data;
36
+ }
37
+ async listLogs(options = {}) {
38
+ const projectId = this.client.projectId;
39
+ const { data } = await this.http.get(`/projects/${projectId}/push/logs`, {
40
+ params: {
41
+ limit: options.limit,
42
+ offset: options.offset,
43
+ status: options.status,
44
+ },
45
+ });
46
+ return data.data;
47
+ }
48
+ async getStats() {
49
+ const projectId = this.client.projectId;
50
+ const { data } = await this.http.get(`/projects/${projectId}/push/stats`);
51
+ return data;
52
+ }
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allanfsouza/aether-sdk",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "SDK do Cliente para a Plataforma Aether",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/auth.ts CHANGED
@@ -2,52 +2,180 @@
2
2
  import type { AxiosInstance } from "axios";
3
3
  import type { PlataformaClient } from "./index.js";
4
4
 
5
+ export interface LoginResponse {
6
+ accessToken: string;
7
+ refreshToken: string;
8
+ user: {
9
+ id: string;
10
+ name: string;
11
+ email: string;
12
+ avatarUrl?: string;
13
+ role?: string;
14
+ aetherRole?: string;
15
+ planCode?: string;
16
+ emailVerified?: boolean;
17
+ };
18
+ }
19
+
20
+ export interface Session {
21
+ id: string;
22
+ userAgent?: string;
23
+ ip?: string;
24
+ createdAt: string;
25
+ expiresAt: string;
26
+ }
27
+
5
28
  export class AuthModule {
6
29
  private client: PlataformaClient;
7
30
  private http: AxiosInstance;
31
+ private refreshToken: string | null = null;
8
32
 
9
33
  constructor(client: PlataformaClient, http: AxiosInstance) {
10
34
  this.client = client;
11
35
  this.http = http;
12
36
  }
13
37
 
14
- async login(email: string, password: string) {
38
+ /**
39
+ * Login com email e senha
40
+ */
41
+ async login(email: string, password: string): Promise<LoginResponse> {
15
42
  try {
16
- const { data } = await this.http.post("/auth/login", { email, password });
17
- if (data.token) {
18
- this.client.setToken(data.token);
43
+ const { data } = await this.http.post<LoginResponse>("/auth/login", {
44
+ email,
45
+ password,
46
+ });
47
+
48
+ if (data.accessToken) {
49
+ this.client.setToken(data.accessToken);
50
+ this.refreshToken = data.refreshToken;
19
51
  }
52
+
20
53
  return data;
21
54
  } catch (e) {
22
55
  this.client.setToken(null);
56
+ this.refreshToken = null;
23
57
  throw e;
24
58
  }
25
59
  }
26
60
 
27
- async register(credentials: { name: string; email: string; password: string }) {
61
+ /**
62
+ * Registrar novo usuário
63
+ */
64
+ async register(credentials: {
65
+ name: string;
66
+ email: string;
67
+ password: string;
68
+ }) {
28
69
  try {
29
70
  const { data } = await this.http.post("/auth/register", credentials);
30
- if (data.token) {
31
- this.client.setToken(data.token);
71
+ return data;
72
+ } catch (e) {
73
+ throw e;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Renovar access token usando refresh token
79
+ */
80
+ async refresh(): Promise<{ accessToken: string }> {
81
+ if (!this.refreshToken) {
82
+ throw new Error("Nenhum refresh token disponível");
83
+ }
84
+
85
+ try {
86
+ const { data } = await this.http.post("/auth/refresh", {
87
+ refreshToken: this.refreshToken,
88
+ });
89
+
90
+ if (data.accessToken) {
91
+ this.client.setToken(data.accessToken);
32
92
  }
93
+
33
94
  return data;
34
95
  } catch (e) {
35
96
  this.client.setToken(null);
97
+ this.refreshToken = null;
36
98
  throw e;
37
99
  }
38
100
  }
39
101
 
102
+ /**
103
+ * Obter URL de autenticação do Google
104
+ */
105
+ getGoogleAuthUrl(): string {
106
+ return `${this.client.apiUrl}/v1/auth/google`;
107
+ }
108
+
109
+ /**
110
+ * Logout da sessão atual
111
+ */
112
+ async logout(): Promise<void> {
113
+ if (this.refreshToken) {
114
+ try {
115
+ await this.http.post("/auth/logout", {
116
+ refreshToken: this.refreshToken,
117
+ });
118
+ } catch (e) {
119
+ // Ignora erro de logout
120
+ }
121
+ }
122
+
123
+ this.client.setToken(null);
124
+ this.refreshToken = null;
125
+ }
126
+
127
+ /**
128
+ * Logout de todas as sessões
129
+ */
130
+ async logoutAll(): Promise<void> {
131
+ try {
132
+ await this.http.post("/auth/logout-all");
133
+ } finally {
134
+ this.client.setToken(null);
135
+ this.refreshToken = null;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Listar sessões ativas
141
+ */
142
+ async getSessions(): Promise<Session[]> {
143
+ const { data } = await this.http.get<{ sessions: Session[] }>(
144
+ "/auth/sessions"
145
+ );
146
+ return data.sessions;
147
+ }
148
+
149
+ /**
150
+ * Esqueci minha senha
151
+ */
40
152
  async forgotPassword(email: string) {
41
153
  const { data } = await this.http.post("/auth/forgot-password", { email });
42
154
  return data;
43
155
  }
44
156
 
157
+ /**
158
+ * Redefinir senha
159
+ */
45
160
  async resetPassword(token: string, newPassword: string) {
46
- const { data } = await this.http.post("/auth/reset-password", { token, newPassword });
161
+ const { data } = await this.http.post("/auth/reset-password", {
162
+ token,
163
+ newPassword,
164
+ });
47
165
  return data;
48
166
  }
49
167
 
50
- logout() {
51
- this.client.setToken(null);
168
+ /**
169
+ * Obter refresh token atual (para armazenamento)
170
+ */
171
+ getRefreshToken(): string | null {
172
+ return this.refreshToken;
173
+ }
174
+
175
+ /**
176
+ * Definir refresh token (para restaurar sessão)
177
+ */
178
+ setRefreshToken(token: string | null) {
179
+ this.refreshToken = token;
52
180
  }
53
181
  }
package/src/database.ts CHANGED
@@ -12,15 +12,130 @@ type WebSocketMessage<T = any> = {
12
12
 
13
13
  // [NOVO] Opções de Listagem Avançada
14
14
  export type ListOptions<T> = {
15
- // Filtro parcial (ex: { status: 'active' })
16
- filter?: Partial<T>;
17
- // Ordenação
15
+ filter?: Partial<T> | Record<string, any>; // Suporta operadores avançados
18
16
  sort?: {
19
- field: keyof T & string; // Garante que o campo existe no tipo T
17
+ field: keyof T & string;
20
18
  order: "ASC" | "DESC";
21
19
  };
20
+ limit?: number;
21
+ offset?: number;
22
22
  };
23
23
 
24
+ /**
25
+ * Builder para construção fluente de queries.
26
+ */
27
+ export class QueryBuilder<T> {
28
+ private collectionRef: CollectionReference<T>;
29
+ private filter: Record<string, any> = {};
30
+ private sort?: { field: string; order: "ASC" | "DESC" };
31
+ private limitVal?: number;
32
+ private offsetVal?: number;
33
+
34
+ constructor(collectionRef: CollectionReference<T>) {
35
+ this.collectionRef = collectionRef;
36
+ }
37
+
38
+ /**
39
+ * Adiciona um filtro de igualdade.
40
+ */
41
+ eq(column: keyof T & string, value: any): this {
42
+ this.filter[column] = value;
43
+ return this;
44
+ }
45
+
46
+ /**
47
+ * Adiciona um filtro de desigualdade ($ne).
48
+ */
49
+ neq(column: keyof T & string, value: any): this {
50
+ this.filter[column] = { ...this.filter[column], $ne: value };
51
+ return this;
52
+ }
53
+
54
+ /**
55
+ * Adiciona um filtro maior que ($gt).
56
+ */
57
+ gt(column: keyof T & string, value: number | string): this {
58
+ this.filter[column] = { ...this.filter[column], $gt: value };
59
+ return this;
60
+ }
61
+
62
+ /**
63
+ * Adiciona um filtro maior ou igual ($gte).
64
+ */
65
+ gte(column: keyof T & string, value: number | string): this {
66
+ this.filter[column] = { ...this.filter[column], $gte: value };
67
+ return this;
68
+ }
69
+
70
+ /**
71
+ * Adiciona um filtro menor que ($lt).
72
+ */
73
+ lt(column: keyof T & string, value: number | string): this {
74
+ this.filter[column] = { ...this.filter[column], $lt: value };
75
+ return this;
76
+ }
77
+
78
+ /**
79
+ * Adiciona um filtro menor ou igual ($lte).
80
+ */
81
+ lte(column: keyof T & string, value: number | string): this {
82
+ this.filter[column] = { ...this.filter[column], $lte: value };
83
+ return this;
84
+ }
85
+
86
+ /**
87
+ * Adiciona um filtro LIKE ($like).
88
+ */
89
+ like(column: keyof T & string, value: string): this {
90
+ this.filter[column] = { ...this.filter[column], $like: value };
91
+ return this;
92
+ }
93
+
94
+ /**
95
+ * Define a ordenação.
96
+ */
97
+ order(column: keyof T & string, direction: "ASC" | "DESC" = "ASC"): this {
98
+ this.sort = { field: column, order: direction };
99
+ return this;
100
+ }
101
+
102
+ /**
103
+ * Define o limite de registros.
104
+ */
105
+ limit(count: number): this {
106
+ this.limitVal = count;
107
+ return this;
108
+ }
109
+
110
+ /**
111
+ * Define o deslocamento (paginação).
112
+ */
113
+ offset(count: number): this {
114
+ this.offsetVal = count;
115
+ return this;
116
+ }
117
+
118
+ /**
119
+ * Executa a query e retorna os resultados.
120
+ */
121
+ async get(): Promise<T[]> {
122
+ return this.collectionRef.list({
123
+ filter: this.filter,
124
+ sort: this.sort as any,
125
+ limit: this.limitVal,
126
+ offset: this.offsetVal,
127
+ });
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Operação em lote.
133
+ */
134
+ export type BatchOperation =
135
+ | { type: "create"; collection: string; data: any }
136
+ | { type: "update"; collection: string; id: string; data: any }
137
+ | { type: "delete"; collection: string; id: string };
138
+
24
139
  /**
25
140
  * Módulo de Banco de Dados
26
141
  */
@@ -41,13 +156,22 @@ export class DatabaseModule {
41
156
  collection<T = any>(collectionName: string) {
42
157
  return new CollectionReference<T>(this.client, this.http, collectionName);
43
158
  }
159
+
160
+ /**
161
+ * Executa múltiplas operações em uma única transação.
162
+ * Se uma falhar, todas são revertidas.
163
+ */
164
+ async batch(operations: BatchOperation[]): Promise<any[]> {
165
+ const { data } = await this.http.post("/db/batch", { operations });
166
+ return data.results;
167
+ }
44
168
  }
45
169
 
46
170
  /**
47
171
  * Referência a uma coleção específica.
48
172
  * O <T> define o formato dos dados (ex: interface User).
49
173
  */
50
- class CollectionReference<T> {
174
+ export class CollectionReference<T> {
51
175
  private client: PlataformaClient;
52
176
  private http: AxiosInstance;
53
177
  private collectionName: string;
@@ -62,6 +186,31 @@ class CollectionReference<T> {
62
186
  this.wsUrl = client.apiUrl.replace(/^https?/, protocol);
63
187
  }
64
188
 
189
+ /**
190
+ * Inicia o QueryBuilder.
191
+ * Atalho para .eq()
192
+ */
193
+ eq(column: keyof T & string, value: any): QueryBuilder<T> {
194
+ return new QueryBuilder<T>(this).eq(column, value);
195
+ }
196
+
197
+ /**
198
+ * Inicia o QueryBuilder.
199
+ * Atalho para .gt()
200
+ */
201
+ gt(column: keyof T & string, value: number | string): QueryBuilder<T> {
202
+ return new QueryBuilder<T>(this).gt(column, value);
203
+ }
204
+
205
+ // ... Outros atalhos podem ser adicionados conforme necessidade ...
206
+
207
+ /**
208
+ * Retorna uma nova instância do QueryBuilder.
209
+ */
210
+ query(): QueryBuilder<T> {
211
+ return new QueryBuilder<T>(this);
212
+ }
213
+
65
214
  /**
66
215
  * Lista documentos da coleção com filtros opcionais.
67
216
  * @param options Filtros e Ordenação
@@ -79,6 +228,10 @@ class CollectionReference<T> {
79
228
  params.sort = JSON.stringify([options.sort.field, options.sort.order]);
80
229
  }
81
230
 
231
+ // TODO: Backend precisa implementar limit/offset na rota GET
232
+ // if (options?.limit) params.limit = options.limit;
233
+ // if (options?.offset) params.offset = options.offset;
234
+
82
235
  const { data } = await this.http.get(`/db/${this.collectionName}`, {
83
236
  params,
84
237
  });
@@ -136,7 +289,7 @@ class CollectionReference<T> {
136
289
 
137
290
  if (!token || !projectId) {
138
291
  console.warn("[SDK] Realtime falhou: Token ou ProjectId ausentes.");
139
- return () => {};
292
+ return () => { };
140
293
  }
141
294
 
142
295
  const url = `${this.wsUrl}/v1/db/subscribe/${this.collectionName}?token=${token}&projectId=${projectId}`;
@@ -173,7 +326,7 @@ class CollectionReference<T> {
173
326
  };
174
327
  } catch (err) {
175
328
  console.error("[SDK] Erro WS:", err);
176
- return () => {};
329
+ return () => { };
177
330
  }
178
331
  }
179
332
  }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ import { AuthModule } from "./auth.js";
5
5
  import { DatabaseModule } from "./database.js";
6
6
  import { StorageModule } from "./storage.js";
7
7
  import { FunctionsModule } from "./functions.js";
8
+ import { PushModule } from "./push.js";
8
9
 
9
10
  /**
10
11
  * Configuração usada para criar o cliente principal da plataforma.
@@ -16,7 +17,7 @@ export type ClientConfig = {
16
17
 
17
18
  /**
18
19
  * O cliente principal da Plataforma API (Aether).
19
- * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions).
20
+ * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions, Push).
20
21
  */
21
22
  export class PlataformaClient {
22
23
  // Módulos públicos disponíveis para o usuário do SDK
@@ -24,13 +25,14 @@ export class PlataformaClient {
24
25
  public db: DatabaseModule;
25
26
  public storage: StorageModule;
26
27
  public functions: FunctionsModule;
28
+ public push: PushModule;
27
29
 
28
30
  // Configurações imutáveis da instância
29
31
  public apiUrl: string;
30
32
  public projectId: string;
31
33
 
32
34
  // Infra interna
33
- private http: AxiosInstance;
35
+ public http: AxiosInstance;
34
36
  private _token: string | null = null;
35
37
 
36
38
  constructor(config: ClientConfig) {
@@ -38,8 +40,8 @@ export class PlataformaClient {
38
40
  throw new Error("apiUrl e projectId são obrigatórios.");
39
41
  }
40
42
 
41
- // Normaliza apiUrl removendo barra final, se houver
42
- this.apiUrl = config.apiUrl.replace(/\/$/, "");
43
+ // Normaliza apiUrl removendo barras finais, se houver
44
+ this.apiUrl = config.apiUrl.replace(/\/+$/, "");
43
45
  this.projectId = config.projectId;
44
46
 
45
47
  // Inicializa o cliente HTTP (passando a própria instância)
@@ -50,6 +52,7 @@ export class PlataformaClient {
50
52
  this.db = new DatabaseModule(this, this.http);
51
53
  this.storage = new StorageModule(this, this.http);
52
54
  this.functions = new FunctionsModule(this, this.http);
55
+ this.push = new PushModule(this, this.http);
53
56
  }
54
57
 
55
58
  // --- Gerenciamento de Token ---
@@ -75,3 +78,15 @@ export class PlataformaClient {
75
78
  export { AetherError } from "./errors.js";
76
79
  export type { ListOptions } from "./database.js";
77
80
 
81
+ // Tipos relacionados a Push
82
+ export type {
83
+ PushPlatform,
84
+ PushEnvironment,
85
+ PushDevice,
86
+ RegisterDeviceParams,
87
+ SendPushResponse,
88
+ PushStatus,
89
+ PushLogEntry,
90
+ ListPushLogsOptions,
91
+ PushStats,
92
+ } from "./push.js";
package/src/push.ts ADDED
@@ -0,0 +1,157 @@
1
+ // src/push.ts
2
+ import type { AxiosInstance } from "axios";
3
+ import type { PlataformaClient } from "./index.js";
4
+
5
+ export type PushPlatform = "android" | "ios" | "web";
6
+ export type PushEnvironment = "dev" | "staging" | "prod";
7
+
8
+ export interface RegisterDeviceParams {
9
+ platform: PushPlatform;
10
+ token: string;
11
+ environment?: PushEnvironment;
12
+ }
13
+
14
+ export interface PushDevice {
15
+ id: string;
16
+ projectId: string;
17
+ userId?: string | null;
18
+ platform: PushPlatform;
19
+ token: string;
20
+ environment: PushEnvironment;
21
+ createdAt: string;
22
+ lastSeenAt: string;
23
+ }
24
+
25
+ export interface RegisterDeviceResponse {
26
+ device: PushDevice;
27
+ }
28
+
29
+ export interface SendPushResponse {
30
+ sent: number;
31
+ total: number;
32
+ message: string;
33
+ }
34
+
35
+ export type PushStatus = "sent" | "failed";
36
+
37
+ export interface PushLogEntry {
38
+ id: string;
39
+ projectId: string;
40
+ userId?: string | null;
41
+ deviceId?: string | null;
42
+ title: string;
43
+ body: string;
44
+ data: Record<string, string>;
45
+ status: PushStatus;
46
+ errorMessage?: string | null;
47
+ createdAt: string;
48
+ }
49
+
50
+ export interface ListPushLogsOptions {
51
+ limit?: number;
52
+ offset?: number;
53
+ status?: PushStatus;
54
+ }
55
+
56
+ export interface PushStats {
57
+ totalDevices: number;
58
+ sent24h: number;
59
+ successRate: number;
60
+ }
61
+
62
+ export class PushModule {
63
+ constructor(
64
+ private client: PlataformaClient,
65
+ private http: AxiosInstance
66
+ ) { }
67
+
68
+ async registerDevice(params: RegisterDeviceParams): Promise<PushDevice> {
69
+ const projectId = this.client.projectId;
70
+
71
+ const { data } = await this.http.post<RegisterDeviceResponse>(
72
+ `/projects/${projectId}/push/devices`,
73
+ {
74
+ platform: params.platform,
75
+ token: params.token,
76
+ environment: params.environment ?? "prod",
77
+ }
78
+ );
79
+
80
+ return data.device;
81
+ }
82
+
83
+ async sendToToken(params: {
84
+ token: string;
85
+ title: string;
86
+ body: string;
87
+ data?: Record<string, string>;
88
+ environment?: PushEnvironment;
89
+ }): Promise<SendPushResponse> {
90
+ const projectId = this.client.projectId;
91
+
92
+ const { data } = await this.http.post<SendPushResponse>(
93
+ `/projects/${projectId}/push/send`,
94
+ {
95
+ token: params.token,
96
+ title: params.title,
97
+ body: params.body,
98
+ data: params.data ?? {},
99
+ environment: params.environment ?? "prod",
100
+ }
101
+ );
102
+
103
+ return data;
104
+ }
105
+
106
+ async sendToUser(params: {
107
+ userId: string;
108
+ title: string;
109
+ body: string;
110
+ data?: Record<string, string>;
111
+ environment?: PushEnvironment;
112
+ }): Promise<SendPushResponse> {
113
+ const projectId = this.client.projectId;
114
+
115
+ const { data } = await this.http.post<SendPushResponse>(
116
+ `/projects/${projectId}/push/send`,
117
+ {
118
+ userId: params.userId,
119
+ title: params.title,
120
+ body: params.body,
121
+ data: params.data ?? {},
122
+ environment: params.environment ?? "prod",
123
+ }
124
+ );
125
+
126
+ return data;
127
+ }
128
+
129
+ async listLogs(
130
+ options: ListPushLogsOptions = {}
131
+ ): Promise<PushLogEntry[]> {
132
+ const projectId = this.client.projectId;
133
+
134
+ const { data } = await this.http.get<{ data: PushLogEntry[] }>(
135
+ `/projects/${projectId}/push/logs`,
136
+ {
137
+ params: {
138
+ limit: options.limit,
139
+ offset: options.offset,
140
+ status: options.status,
141
+ },
142
+ }
143
+ );
144
+
145
+ return data.data;
146
+ }
147
+
148
+ async getStats(): Promise<PushStats> {
149
+ const projectId = this.client.projectId;
150
+
151
+ const { data } = await this.http.get<PushStats>(
152
+ `/projects/${projectId}/push/stats`
153
+ );
154
+
155
+ return data;
156
+ }
157
+ }