@allanfsouza/aether-sdk 2.1.0 → 2.3.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 +65 -2
- package/dist/auth.js +98 -8
- package/dist/database.d.ts +95 -3
- package/dist/database.js +123 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -3
- package/dist/push.d.ts +72 -0
- package/dist/push.js +54 -0
- package/package.json +6 -2
- package/src/auth.ts +138 -10
- package/src/database.ts +160 -7
- package/src/index.ts +19 -4
- package/src/push.ts +158 -0
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
|
-
|
|
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
|
-
|
|
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", {
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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", {
|
|
115
|
+
const { data } = await this.http.post("/auth/reset-password", {
|
|
116
|
+
token,
|
|
117
|
+
newPassword,
|
|
118
|
+
});
|
|
38
119
|
return data;
|
|
39
120
|
}
|
|
40
|
-
|
|
41
|
-
|
|
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
|
}
|
package/dist/database.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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,54 @@
|
|
|
1
|
+
import { routes } from "@allanfsouza/aether-shared";
|
|
2
|
+
export class PushModule {
|
|
3
|
+
constructor(client, http) {
|
|
4
|
+
this.client = client;
|
|
5
|
+
this.http = http;
|
|
6
|
+
}
|
|
7
|
+
async registerDevice(params) {
|
|
8
|
+
const projectId = this.client.projectId;
|
|
9
|
+
const { data } = await this.http.post(routes.push.devices.build(projectId), {
|
|
10
|
+
platform: params.platform,
|
|
11
|
+
token: params.token,
|
|
12
|
+
environment: params.environment ?? "prod",
|
|
13
|
+
});
|
|
14
|
+
return data.device;
|
|
15
|
+
}
|
|
16
|
+
async sendToToken(params) {
|
|
17
|
+
const projectId = this.client.projectId;
|
|
18
|
+
const { data } = await this.http.post(routes.push.send.build(projectId), {
|
|
19
|
+
token: params.token,
|
|
20
|
+
title: params.title,
|
|
21
|
+
body: params.body,
|
|
22
|
+
data: params.data ?? {},
|
|
23
|
+
environment: params.environment ?? "prod",
|
|
24
|
+
});
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
async sendToUser(params) {
|
|
28
|
+
const projectId = this.client.projectId;
|
|
29
|
+
const { data } = await this.http.post(routes.push.send.build(projectId), {
|
|
30
|
+
userId: params.userId,
|
|
31
|
+
title: params.title,
|
|
32
|
+
body: params.body,
|
|
33
|
+
data: params.data ?? {},
|
|
34
|
+
environment: params.environment ?? "prod",
|
|
35
|
+
});
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
async listLogs(options = {}) {
|
|
39
|
+
const projectId = this.client.projectId;
|
|
40
|
+
const { data } = await this.http.get(routes.push.logs.build(projectId), {
|
|
41
|
+
params: {
|
|
42
|
+
limit: options.limit,
|
|
43
|
+
offset: options.offset,
|
|
44
|
+
status: options.status,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
return data.data;
|
|
48
|
+
}
|
|
49
|
+
async getStats() {
|
|
50
|
+
const projectId = this.client.projectId;
|
|
51
|
+
const { data } = await this.http.get(routes.push.stats.build(projectId));
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allanfsouza/aether-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "SDK do Cliente para a Plataforma Aether",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -23,9 +23,13 @@
|
|
|
23
23
|
"author": "",
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"
|
|
26
|
+
"@allanfsouza/aether-shared": "file:../aether-shared",
|
|
27
|
+
"axios": "^1.6.0",
|
|
28
|
+
"ws": "^8.16.0"
|
|
27
29
|
},
|
|
28
30
|
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"@types/ws": "^8.5.10",
|
|
29
33
|
"typescript": "^5.3.0"
|
|
30
34
|
}
|
|
31
35
|
}
|
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
|
-
|
|
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", {
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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", {
|
|
161
|
+
const { data } = await this.http.post("/auth/reset-password", {
|
|
162
|
+
token,
|
|
163
|
+
newPassword,
|
|
164
|
+
});
|
|
47
165
|
return data;
|
|
48
166
|
}
|
|
49
167
|
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
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,158 @@
|
|
|
1
|
+
// src/push.ts
|
|
2
|
+
import type { AxiosInstance } from "axios";
|
|
3
|
+
import type { PlataformaClient } from "./index.js";
|
|
4
|
+
import { routes } from "@allanfsouza/aether-shared";
|
|
5
|
+
|
|
6
|
+
export type PushPlatform = "android" | "ios" | "web";
|
|
7
|
+
export type PushEnvironment = "dev" | "staging" | "prod";
|
|
8
|
+
|
|
9
|
+
export interface RegisterDeviceParams {
|
|
10
|
+
platform: PushPlatform;
|
|
11
|
+
token: string;
|
|
12
|
+
environment?: PushEnvironment;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PushDevice {
|
|
16
|
+
id: string;
|
|
17
|
+
projectId: string;
|
|
18
|
+
userId?: string | null;
|
|
19
|
+
platform: PushPlatform;
|
|
20
|
+
token: string;
|
|
21
|
+
environment: PushEnvironment;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
lastSeenAt: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RegisterDeviceResponse {
|
|
27
|
+
device: PushDevice;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SendPushResponse {
|
|
31
|
+
sent: number;
|
|
32
|
+
total: number;
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PushStatus = "sent" | "failed";
|
|
37
|
+
|
|
38
|
+
export interface PushLogEntry {
|
|
39
|
+
id: string;
|
|
40
|
+
projectId: string;
|
|
41
|
+
userId?: string | null;
|
|
42
|
+
deviceId?: string | null;
|
|
43
|
+
title: string;
|
|
44
|
+
body: string;
|
|
45
|
+
data: Record<string, string>;
|
|
46
|
+
status: PushStatus;
|
|
47
|
+
errorMessage?: string | null;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ListPushLogsOptions {
|
|
52
|
+
limit?: number;
|
|
53
|
+
offset?: number;
|
|
54
|
+
status?: PushStatus;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface PushStats {
|
|
58
|
+
totalDevices: number;
|
|
59
|
+
sent24h: number;
|
|
60
|
+
successRate: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class PushModule {
|
|
64
|
+
constructor(
|
|
65
|
+
private client: PlataformaClient,
|
|
66
|
+
private http: AxiosInstance
|
|
67
|
+
) { }
|
|
68
|
+
|
|
69
|
+
async registerDevice(params: RegisterDeviceParams): Promise<PushDevice> {
|
|
70
|
+
const projectId = this.client.projectId;
|
|
71
|
+
|
|
72
|
+
const { data } = await this.http.post<RegisterDeviceResponse>(
|
|
73
|
+
routes.push.devices.build(projectId),
|
|
74
|
+
{
|
|
75
|
+
platform: params.platform,
|
|
76
|
+
token: params.token,
|
|
77
|
+
environment: params.environment ?? "prod",
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return data.device;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async sendToToken(params: {
|
|
85
|
+
token: string;
|
|
86
|
+
title: string;
|
|
87
|
+
body: string;
|
|
88
|
+
data?: Record<string, string>;
|
|
89
|
+
environment?: PushEnvironment;
|
|
90
|
+
}): Promise<SendPushResponse> {
|
|
91
|
+
const projectId = this.client.projectId;
|
|
92
|
+
|
|
93
|
+
const { data } = await this.http.post<SendPushResponse>(
|
|
94
|
+
routes.push.send.build(projectId),
|
|
95
|
+
{
|
|
96
|
+
token: params.token,
|
|
97
|
+
title: params.title,
|
|
98
|
+
body: params.body,
|
|
99
|
+
data: params.data ?? {},
|
|
100
|
+
environment: params.environment ?? "prod",
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return data;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async sendToUser(params: {
|
|
108
|
+
userId: string;
|
|
109
|
+
title: string;
|
|
110
|
+
body: string;
|
|
111
|
+
data?: Record<string, string>;
|
|
112
|
+
environment?: PushEnvironment;
|
|
113
|
+
}): Promise<SendPushResponse> {
|
|
114
|
+
const projectId = this.client.projectId;
|
|
115
|
+
|
|
116
|
+
const { data } = await this.http.post<SendPushResponse>(
|
|
117
|
+
routes.push.send.build(projectId),
|
|
118
|
+
{
|
|
119
|
+
userId: params.userId,
|
|
120
|
+
title: params.title,
|
|
121
|
+
body: params.body,
|
|
122
|
+
data: params.data ?? {},
|
|
123
|
+
environment: params.environment ?? "prod",
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return data;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async listLogs(
|
|
131
|
+
options: ListPushLogsOptions = {}
|
|
132
|
+
): Promise<PushLogEntry[]> {
|
|
133
|
+
const projectId = this.client.projectId;
|
|
134
|
+
|
|
135
|
+
const { data } = await this.http.get<{ data: PushLogEntry[] }>(
|
|
136
|
+
routes.push.logs.build(projectId),
|
|
137
|
+
{
|
|
138
|
+
params: {
|
|
139
|
+
limit: options.limit,
|
|
140
|
+
offset: options.offset,
|
|
141
|
+
status: options.status,
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return data.data;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getStats(): Promise<PushStats> {
|
|
150
|
+
const projectId = this.client.projectId;
|
|
151
|
+
|
|
152
|
+
const { data } = await this.http.get<PushStats>(
|
|
153
|
+
routes.push.stats.build(projectId)
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
}
|