@gzl10/baserow 1.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/CHANGELOG.md +435 -0
- package/README.md +847 -0
- package/dist/index.d.ts +8749 -0
- package/dist/index.js +11167 -0
- package/dist/index.js.map +1 -0
- package/package.json +91 -0
- package/src/BaserowClient.ts +501 -0
- package/src/ClientWithCreds.ts +545 -0
- package/src/ClientWithCredsWs.ts +852 -0
- package/src/ClientWithToken.ts +171 -0
- package/src/contexts/DatabaseClientContext.ts +114 -0
- package/src/contexts/DatabaseContext.ts +870 -0
- package/src/contexts/DatabaseTokenContext.ts +331 -0
- package/src/contexts/FieldContext.ts +399 -0
- package/src/contexts/RowContext.ts +99 -0
- package/src/contexts/TableClientContext.ts +291 -0
- package/src/contexts/TableContext.ts +1247 -0
- package/src/contexts/TableOnlyContext.ts +74 -0
- package/src/contexts/WorkspaceContext.ts +490 -0
- package/src/express/errors.ts +260 -0
- package/src/express/index.ts +69 -0
- package/src/express/middleware.ts +225 -0
- package/src/express/serializers.ts +314 -0
- package/src/index.ts +247 -0
- package/src/presets/performance.ts +262 -0
- package/src/services/AuthService.ts +472 -0
- package/src/services/DatabaseService.ts +246 -0
- package/src/services/DatabaseTokenService.ts +186 -0
- package/src/services/FieldService.ts +1543 -0
- package/src/services/RowService.ts +982 -0
- package/src/services/SchemaControlService.ts +420 -0
- package/src/services/TableService.ts +781 -0
- package/src/services/WorkspaceService.ts +113 -0
- package/src/services/core/BaseAuthClient.ts +111 -0
- package/src/services/core/BaseClient.ts +107 -0
- package/src/services/core/BaseService.ts +71 -0
- package/src/services/core/HttpService.ts +115 -0
- package/src/services/core/ValidationService.ts +149 -0
- package/src/types/auth.ts +177 -0
- package/src/types/core.ts +91 -0
- package/src/types/errors.ts +105 -0
- package/src/types/fields.ts +456 -0
- package/src/types/index.ts +222 -0
- package/src/types/requests.ts +333 -0
- package/src/types/responses.ts +50 -0
- package/src/types/schema.ts +446 -0
- package/src/types/tokens.ts +36 -0
- package/src/types.ts +11 -0
- package/src/utils/auth.ts +174 -0
- package/src/utils/axios.ts +647 -0
- package/src/utils/field-cache.ts +164 -0
- package/src/utils/httpFactory.ts +66 -0
- package/src/utils/jwt-decoder.ts +188 -0
- package/src/utils/jwtTokens.ts +50 -0
- package/src/utils/performance.ts +105 -0
- package/src/utils/prisma-mapper.ts +961 -0
- package/src/utils/validation.ts +463 -0
- package/src/validators/schema.ts +419 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { BaserowAdminConfig, LoginResponse, BaserowConfigError } from './types'
|
|
2
|
+
import { createHttpClient } from './utils/httpFactory'
|
|
3
|
+
import { PerformanceManager } from './utils/performance'
|
|
4
|
+
import { setupJwtTokens } from './utils/jwtTokens'
|
|
5
|
+
import { validateRequired, validateString, validateUrl } from './utils/validation'
|
|
6
|
+
import { AuthService } from './services/AuthService'
|
|
7
|
+
import { WorkspaceService } from './services/WorkspaceService'
|
|
8
|
+
import { DatabaseService } from './services/DatabaseService'
|
|
9
|
+
import { DatabaseTokenService } from './services/DatabaseTokenService'
|
|
10
|
+
import { TableService } from './services/TableService'
|
|
11
|
+
import { FieldService } from './services/FieldService'
|
|
12
|
+
import { RowService } from './services/RowService'
|
|
13
|
+
import { WorkspaceContext } from './contexts/WorkspaceContext'
|
|
14
|
+
import { BaseAuthClient } from './services/core/BaseAuthClient'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Cliente administrativo con acceso global a todos los workspaces
|
|
18
|
+
*
|
|
19
|
+
* Proporciona acceso completo de administración usando JWT authentication con auto-refresh.
|
|
20
|
+
* API jerárquica que siempre comienza por workspace para mantener contexto claro.
|
|
21
|
+
* Ideal para scripts de administración y operaciones multi-workspace.
|
|
22
|
+
*
|
|
23
|
+
* **Características:**
|
|
24
|
+
* - Autenticación JWT con auto-refresh automático
|
|
25
|
+
* - API jerárquica: siempre `admin.workspace(name).database(name)`
|
|
26
|
+
* - Acceso global a todos los workspaces del usuario
|
|
27
|
+
* - Operaciones administrativas completas: crear/modificar estructura
|
|
28
|
+
* - Resolución automática de nombres a IDs
|
|
29
|
+
* - Validación de permisos y contextos
|
|
30
|
+
*
|
|
31
|
+
* **API Jerárquica:**
|
|
32
|
+
* - `admin.workspaces` → operaciones masivas de workspaces
|
|
33
|
+
* - `admin.workspace(name)` → contexto de workspace específico
|
|
34
|
+
* - `admin.workspace(name).databases` → operaciones masivas de databases
|
|
35
|
+
* - `admin.workspace(name).database(name)` → contexto de database específica
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* // Crear cliente admin global usando la API unificada
|
|
40
|
+
* const admin = await BaserowClient.create({
|
|
41
|
+
* url: 'https://baserow.example.com',
|
|
42
|
+
* credentials: {
|
|
43
|
+
* email: 'admin@example.com',
|
|
44
|
+
* password: 'password123'
|
|
45
|
+
* }
|
|
46
|
+
* })
|
|
47
|
+
*
|
|
48
|
+
* // API jerárquica que siempre comienza por workspace
|
|
49
|
+
* const workspaces = await admin.workspaces.findMany()
|
|
50
|
+
* const workspace = await admin.workspaces.create({ name: 'New Workspace' })
|
|
51
|
+
*
|
|
52
|
+
* // Gestión completa de workspaces con API jerárquica
|
|
53
|
+
* const updatedWS = await admin.workspace('Company').update({ name: 'New Company' })
|
|
54
|
+
* await admin.workspace('Old Company').delete()
|
|
55
|
+
*
|
|
56
|
+
* // Operaciones específicas con contexto jerárquico
|
|
57
|
+
* const database = await admin.workspace('Company')
|
|
58
|
+
* .database('CRM')
|
|
59
|
+
* .create()
|
|
60
|
+
*
|
|
61
|
+
* const table = await admin.workspace('Company')
|
|
62
|
+
* .database('CRM')
|
|
63
|
+
* .table('Customers')
|
|
64
|
+
* .create({
|
|
65
|
+
* data: [['Name', 'Email'], ['Alice', 'alice@example.com']],
|
|
66
|
+
* first_row_header: true
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @since 1.0.0
|
|
71
|
+
*/
|
|
72
|
+
export class ClientWithCreds extends BaseAuthClient<BaserowAdminConfig, 'credentials'> {
|
|
73
|
+
// Servicios para operaciones globales
|
|
74
|
+
public readonly workspaces: WorkspaceService
|
|
75
|
+
private readonly databaseService: DatabaseService
|
|
76
|
+
private readonly databaseTokenService: DatabaseTokenService
|
|
77
|
+
private readonly tableService: TableService
|
|
78
|
+
private readonly fieldService: FieldService
|
|
79
|
+
private readonly rowService: RowService
|
|
80
|
+
|
|
81
|
+
private constructor(config: BaserowAdminConfig, loginResponse: LoginResponse) {
|
|
82
|
+
// Procesar performance con defaults antes de guardar
|
|
83
|
+
const processedConfig = {
|
|
84
|
+
...config,
|
|
85
|
+
performance: PerformanceManager.merge(config.performance)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Inicializar HttpClient con JWT token
|
|
89
|
+
const http = createHttpClient({
|
|
90
|
+
url: processedConfig.url,
|
|
91
|
+
token: loginResponse.access_token,
|
|
92
|
+
logger: processedConfig.logger,
|
|
93
|
+
performance: processedConfig.performance
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Inicializar AuthService
|
|
97
|
+
const auth = new AuthService(http, processedConfig.logger)
|
|
98
|
+
|
|
99
|
+
super(processedConfig, http, auth, processedConfig.logger)
|
|
100
|
+
|
|
101
|
+
// Transferir tokens al AuthService ANTES de configurar auto-refresh
|
|
102
|
+
setupJwtTokens(this.auth, loginResponse)
|
|
103
|
+
|
|
104
|
+
// Configurar auto-refresh de tokens (ahora tiene los tokens almacenados)
|
|
105
|
+
this.auth.setupAutoRefresh()
|
|
106
|
+
|
|
107
|
+
// Iniciar keep-alive si está habilitado
|
|
108
|
+
if (processedConfig.keepAlive?.enabled) {
|
|
109
|
+
const intervalMinutes = processedConfig.keepAlive.intervalMinutes ?? 7200
|
|
110
|
+
this.auth.startKeepAlive(intervalMinutes)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Inicializar servicios
|
|
114
|
+
this.workspaces = new WorkspaceService(this.http, this.logger)
|
|
115
|
+
this.databaseService = new DatabaseService(this.http, this.logger)
|
|
116
|
+
this.databaseTokenService = new DatabaseTokenService(this.http, this.logger)
|
|
117
|
+
this.tableService = new TableService(this.http, this.logger)
|
|
118
|
+
this.fieldService = new FieldService(this.http, this.logger)
|
|
119
|
+
this.rowService = new RowService(this.http, this.logger)
|
|
120
|
+
|
|
121
|
+
if (this.logger) {
|
|
122
|
+
this.logger.info('ClientWithCreds initialized with JWT authentication (global mode)')
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Campos que deben excluirse por defecto para cliente con credenciales
|
|
128
|
+
* Excluye credentials por seguridad
|
|
129
|
+
*/
|
|
130
|
+
protected getDefaultExcludeKeys(): string[] {
|
|
131
|
+
return ['credentials']
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Crear instancia de ClientWithCreds con auto-login
|
|
136
|
+
*
|
|
137
|
+
* Factory method que maneja la autenticación automáticamente y retorna una instancia
|
|
138
|
+
* lista para usar con JWT token válido y auto-refresh configurado.
|
|
139
|
+
*
|
|
140
|
+
* **Keep-Alive opcional** para backends 24/7:
|
|
141
|
+
* - Habilitar con `keepAlive: { enabled: true }` para evitar expiración de refresh token
|
|
142
|
+
* - Default: re-login cada 5 días (7200 min), margen de seguridad de 2 días
|
|
143
|
+
* - ⚠️ Almacena credenciales en memoria (solo para backends confiables)
|
|
144
|
+
*
|
|
145
|
+
* @param config - Configuración del cliente administrativo
|
|
146
|
+
* @param config.url - URL del servidor Baserow
|
|
147
|
+
* @param config.credentials - Credenciales de usuario (email/password)
|
|
148
|
+
* @param config.keepAlive - Opcional: configuración keep-alive para backends 24/7
|
|
149
|
+
* @param config.logger - Logger opcional para debugging
|
|
150
|
+
* @returns Promise que resuelve a instancia autenticada de ClientWithCreds
|
|
151
|
+
*
|
|
152
|
+
* @throws {BaserowConfigError} Si la configuración es inválida
|
|
153
|
+
* @throws {BaserowAuthError} Si las credenciales son incorrectas
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* // Cliente básico sin keep-alive
|
|
158
|
+
* const admin = await BaserowClient.create({
|
|
159
|
+
* url: 'https://baserow.example.com',
|
|
160
|
+
* credentials: {
|
|
161
|
+
* email: 'admin@example.com',
|
|
162
|
+
* password: 'secure-password'
|
|
163
|
+
* }
|
|
164
|
+
* })
|
|
165
|
+
*
|
|
166
|
+
* // Cliente con keep-alive para backend 24/7 (re-login cada 5 días)
|
|
167
|
+
* const adminBackend = await BaserowClient.create({
|
|
168
|
+
* url: 'https://baserow.example.com',
|
|
169
|
+
* credentials: {
|
|
170
|
+
* email: 'admin@example.com',
|
|
171
|
+
* password: 'secure-password'
|
|
172
|
+
* },
|
|
173
|
+
* keepAlive: { enabled: true }
|
|
174
|
+
* })
|
|
175
|
+
*
|
|
176
|
+
* // Cliente listo para usar
|
|
177
|
+
* const workspaces = await admin.workspaces.findMany()
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @since 1.0.0
|
|
181
|
+
*/
|
|
182
|
+
static async create(config: BaserowAdminConfig): Promise<ClientWithCreds> {
|
|
183
|
+
validateRequired(config, 'config')
|
|
184
|
+
validateString(config.url, 'url')
|
|
185
|
+
validateRequired(config.credentials, 'credentials')
|
|
186
|
+
validateString(config.credentials.email, 'email')
|
|
187
|
+
validateString(config.credentials.password, 'password')
|
|
188
|
+
|
|
189
|
+
if (!validateUrl(config.url)) {
|
|
190
|
+
throw new BaserowConfigError('Invalid URL format', 'url')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Crear HttpClient temporal para login
|
|
194
|
+
const tempHttp = createHttpClient({
|
|
195
|
+
url: config.url,
|
|
196
|
+
token: '', // Sin token inicial
|
|
197
|
+
logger: config.logger,
|
|
198
|
+
performance: config.performance
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Crear AuthService temporal
|
|
202
|
+
const tempAuth = new AuthService(tempHttp, config.logger)
|
|
203
|
+
|
|
204
|
+
// Realizar login
|
|
205
|
+
const loginResponse = await tempAuth.login(config.credentials)
|
|
206
|
+
|
|
207
|
+
if (config.logger) {
|
|
208
|
+
config.logger.info(`ClientWithCreds: Logged in as ${loginResponse.user.username}`)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Crear instancia con token válido
|
|
212
|
+
// Los tokens son transferidos dentro del constructor
|
|
213
|
+
const admin = new ClientWithCreds(config, loginResponse)
|
|
214
|
+
|
|
215
|
+
return admin
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Verificar estado de salud del servidor Baserow
|
|
220
|
+
*
|
|
221
|
+
* Realiza un health check del servidor sin requerir autenticación.
|
|
222
|
+
* Útil para verificar conectividad antes de realizar operaciones.
|
|
223
|
+
*
|
|
224
|
+
* @returns Promise que resuelve a `true` si el servidor está saludable, `false` en caso contrario
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* const isHealthy = await admin.health()
|
|
229
|
+
* if (!isHealthy) {
|
|
230
|
+
* console.log('Servidor Baserow no disponible')
|
|
231
|
+
* return
|
|
232
|
+
* }
|
|
233
|
+
*
|
|
234
|
+
* // Proceder con operaciones
|
|
235
|
+
* const workspaces = await admin.workspaces.findMany()
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* @since 1.0.0
|
|
239
|
+
*/
|
|
240
|
+
async health(): Promise<boolean> {
|
|
241
|
+
try {
|
|
242
|
+
// Usar endpoint de salud oficial (no requiere autenticación)
|
|
243
|
+
const healthUrl = `${this.config.url.replace(/\/$/, '')}/api/_health/`
|
|
244
|
+
const response = await fetch(healthUrl, {
|
|
245
|
+
method: 'GET',
|
|
246
|
+
headers: { 'Content-Type': 'application/json' }
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
return response.ok
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (this.logger) {
|
|
252
|
+
this.logger.error('ClientWithCreds health check failed:', error)
|
|
253
|
+
}
|
|
254
|
+
return false
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Obtener la configuración actual del cliente (sin exponer credenciales)
|
|
260
|
+
*
|
|
261
|
+
* Retorna una copia de solo lectura de la configuración, excluyendo las credenciales
|
|
262
|
+
* por seguridad. Útil para debugging y logging.
|
|
263
|
+
*
|
|
264
|
+
* @returns Configuración actual sin credenciales (solo lectura)
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* const config = admin.getConfig()
|
|
269
|
+
* console.log('URL servidor:', config.url)
|
|
270
|
+
* // Las credenciales no están incluidas por seguridad
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
273
|
+
* @since 1.0.0
|
|
274
|
+
*/
|
|
275
|
+
getConfig(): Readonly<Omit<BaserowAdminConfig, 'credentials'>> {
|
|
276
|
+
return this.getConfigBase()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Verificar si el cliente está autenticado
|
|
281
|
+
*
|
|
282
|
+
* Comprueba si hay un token JWT válido y no expirado.
|
|
283
|
+
* Si el token está próximo a expirar, el auto-refresh se encargará de renovarlo.
|
|
284
|
+
*
|
|
285
|
+
* @returns `true` si está autenticado con token válido, `false` en caso contrario
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* if (admin.isAuthenticated()) {
|
|
290
|
+
* const workspaces = await admin.workspaces.findMany()
|
|
291
|
+
* } else {
|
|
292
|
+
* console.log('Sesión expirada, necesario re-login')
|
|
293
|
+
* }
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @since 1.0.0
|
|
297
|
+
*/
|
|
298
|
+
isAuthenticated(): boolean {
|
|
299
|
+
return this.auth.isAuthenticated()
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Obtener el token JWT actual
|
|
304
|
+
*
|
|
305
|
+
* Retorna el token de acceso actual si está disponible.
|
|
306
|
+
* Útil para debugging o integración con otros sistemas.
|
|
307
|
+
*
|
|
308
|
+
* @returns Token JWT actual o `undefined` si no está autenticado
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* const token = admin.getCurrentToken()
|
|
313
|
+
* if (token) {
|
|
314
|
+
* console.log('Token disponible:', token.substring(0, 20) + '...')
|
|
315
|
+
* }
|
|
316
|
+
* ```
|
|
317
|
+
*
|
|
318
|
+
* @since 1.0.0
|
|
319
|
+
*/
|
|
320
|
+
getCurrentToken(): string | undefined {
|
|
321
|
+
return this.auth.getCurrentToken()
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Renovar el token de acceso manualmente
|
|
326
|
+
*
|
|
327
|
+
* Fuerza la renovación del token JWT usando el refresh token.
|
|
328
|
+
* Normalmente no es necesario llamar esto manualmente ya que el auto-refresh
|
|
329
|
+
* se encarga automáticamente.
|
|
330
|
+
*
|
|
331
|
+
* @returns Promise que resuelve al nuevo token de acceso
|
|
332
|
+
*
|
|
333
|
+
* @throws {BaserowAuthError} Si el refresh token es inválido o expirado
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* try {
|
|
338
|
+
* const newToken = await admin.refreshToken()
|
|
339
|
+
* console.log('Token renovado exitosamente')
|
|
340
|
+
* } catch (error) {
|
|
341
|
+
* console.log('Error renovando token, necesario re-login')
|
|
342
|
+
* }
|
|
343
|
+
* ```
|
|
344
|
+
*
|
|
345
|
+
* @since 1.0.0
|
|
346
|
+
*/
|
|
347
|
+
async refreshToken(): Promise<string> {
|
|
348
|
+
return await this.auth.refreshAccessToken()
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Verificar si el cliente necesita re-autenticación
|
|
353
|
+
*
|
|
354
|
+
* Útil para detectar si los tokens se han perdido o el refresh token expiró,
|
|
355
|
+
* permitiendo manejar proactivamente la re-autenticación antes de hacer requests.
|
|
356
|
+
*
|
|
357
|
+
* @returns `true` si necesita re-login, `false` si la sesión es válida
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* if (admin.needsReLogin()) {
|
|
362
|
+
* console.log('Sesión expirada, solicitando credenciales...')
|
|
363
|
+
* const newAdmin = await ClientWithCreds.create({
|
|
364
|
+
* url: config.url,
|
|
365
|
+
* credentials: { email, password }
|
|
366
|
+
* })
|
|
367
|
+
* } else {
|
|
368
|
+
* const workspaces = await admin.workspaces.findMany()
|
|
369
|
+
* }
|
|
370
|
+
* ```
|
|
371
|
+
*
|
|
372
|
+
* @since 1.0.4
|
|
373
|
+
*/
|
|
374
|
+
needsReLogin(): boolean {
|
|
375
|
+
if (!this.auth.getCurrentToken() || !this.auth['refreshToken']) {
|
|
376
|
+
return true
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (this.auth.isTokenExpired() && !this.auth['refreshToken']) {
|
|
380
|
+
return true
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return false
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Obtener estado detallado de autenticación
|
|
388
|
+
*
|
|
389
|
+
* @returns Objeto con información detallada del estado de autenticación
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```typescript
|
|
393
|
+
* const status = admin.getAuthStatus()
|
|
394
|
+
* console.log('Autenticado:', status.isAuthenticated)
|
|
395
|
+
* console.log('Necesita re-login:', status.needsReLogin)
|
|
396
|
+
* ```
|
|
397
|
+
*
|
|
398
|
+
* @since 1.0.4
|
|
399
|
+
*/
|
|
400
|
+
getAuthStatus(): {
|
|
401
|
+
isAuthenticated: boolean
|
|
402
|
+
hasAccessToken: boolean
|
|
403
|
+
hasRefreshToken: boolean
|
|
404
|
+
tokenExpiry?: Date
|
|
405
|
+
isTokenExpired: boolean
|
|
406
|
+
needsReLogin: boolean
|
|
407
|
+
} {
|
|
408
|
+
const hasAccessToken = !!this.auth.getCurrentToken()
|
|
409
|
+
const hasRefreshToken = !!this.auth['refreshToken']
|
|
410
|
+
const tokenExpiry = this.auth['tokenExpiry'] as Date | undefined
|
|
411
|
+
const isTokenExpired = this.auth.isTokenExpired()
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
isAuthenticated: this.auth.isAuthenticated(),
|
|
415
|
+
hasAccessToken,
|
|
416
|
+
hasRefreshToken,
|
|
417
|
+
tokenExpiry,
|
|
418
|
+
isTokenExpired,
|
|
419
|
+
needsReLogin: this.needsReLogin()
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Cerrar sesión y limpiar tokens
|
|
425
|
+
*
|
|
426
|
+
* Invalida los tokens JWT, detiene el keep-alive y limpia el estado de autenticación.
|
|
427
|
+
* Después de llamar este método, el cliente necesitará volver a autenticarse.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```typescript
|
|
431
|
+
* // Al final de la aplicación o cambio de usuario
|
|
432
|
+
* admin.logout()
|
|
433
|
+
*
|
|
434
|
+
* // El cliente ya no está autenticado
|
|
435
|
+
* console.log(admin.isAuthenticated()) // false
|
|
436
|
+
* ```
|
|
437
|
+
*
|
|
438
|
+
* @since 1.0.0
|
|
439
|
+
*/
|
|
440
|
+
logout(): void {
|
|
441
|
+
this.auth.logout() // Esto internamente llama stopKeepAlive()
|
|
442
|
+
if (this.logger) {
|
|
443
|
+
this.logger.info('ClientWithCreds: Logged out')
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ============================================================================
|
|
448
|
+
// API JERÁRQUICA - SIEMPRE COMIENZA POR WORKSPACE
|
|
449
|
+
// ============================================================================
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Acceder a un workspace específico (API jerárquica)
|
|
453
|
+
*
|
|
454
|
+
* Crea un contexto de workspace que permite operaciones en databases y recursos anidados.
|
|
455
|
+
* Acepta tanto nombres de workspace como IDs numéricos.
|
|
456
|
+
*
|
|
457
|
+
* @param workspaceIdentifier - Nombre del workspace o ID numérico
|
|
458
|
+
* @returns Contexto de workspace con acceso a databases y operaciones
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* // Usando nombre del workspace
|
|
463
|
+
* const databases = await admin.workspace('My Workspace').databases.list()
|
|
464
|
+
* const database = await admin.workspace('My Workspace').databases.create({
|
|
465
|
+
* name: 'New Database'
|
|
466
|
+
* })
|
|
467
|
+
*
|
|
468
|
+
* // Usando ID numérico
|
|
469
|
+
* const tables = await admin.workspace(123).database('My DB').tables.findMany()
|
|
470
|
+
*
|
|
471
|
+
* // API jerárquica completa
|
|
472
|
+
* const table = await admin.workspace('Company')
|
|
473
|
+
* .database('CRM')
|
|
474
|
+
* .table('Customers')
|
|
475
|
+
* .create({
|
|
476
|
+
* data: [['Name', 'Email'], ['John', 'john@example.com']],
|
|
477
|
+
* first_row_header: true
|
|
478
|
+
* })
|
|
479
|
+
* ```
|
|
480
|
+
*
|
|
481
|
+
* @since 1.0.0
|
|
482
|
+
*/
|
|
483
|
+
workspace(workspaceIdentifier: string | number): WorkspaceContext {
|
|
484
|
+
return new WorkspaceContext(
|
|
485
|
+
this.workspaces,
|
|
486
|
+
this.databaseService,
|
|
487
|
+
this.databaseTokenService,
|
|
488
|
+
this.tableService,
|
|
489
|
+
this.fieldService,
|
|
490
|
+
this.rowService,
|
|
491
|
+
workspaceIdentifier,
|
|
492
|
+
this.logger
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Obtener estadísticas de uso global
|
|
498
|
+
*/
|
|
499
|
+
async getUsageStats(): Promise<{
|
|
500
|
+
totalWorkspaces: number
|
|
501
|
+
totalDatabases: number
|
|
502
|
+
totalTables: number
|
|
503
|
+
isAuthenticated: boolean
|
|
504
|
+
tokenValid: boolean
|
|
505
|
+
mode: 'global'
|
|
506
|
+
}> {
|
|
507
|
+
const workspaces = await this.workspaces.findMany()
|
|
508
|
+
|
|
509
|
+
// Contar databases y tables de todos los workspaces
|
|
510
|
+
let totalDatabases = 0
|
|
511
|
+
let totalTables = 0
|
|
512
|
+
|
|
513
|
+
for (const workspace of workspaces) {
|
|
514
|
+
const databases = await this.databaseService.findMany()
|
|
515
|
+
const workspaceDatabases = databases.filter(db => db.workspace?.id === workspace.id)
|
|
516
|
+
totalDatabases += workspaceDatabases.length
|
|
517
|
+
|
|
518
|
+
// Usar las tablas que ya vienen en el objeto Database (más eficiente)
|
|
519
|
+
totalTables += workspaceDatabases.reduce((sum, db) => sum + (db.tables?.length || 0), 0)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
totalWorkspaces: workspaces.length,
|
|
524
|
+
totalDatabases,
|
|
525
|
+
totalTables,
|
|
526
|
+
isAuthenticated: this.isAuthenticated(),
|
|
527
|
+
tokenValid: !this.auth.isTokenExpired(),
|
|
528
|
+
mode: 'global'
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Cerrar conexiones HTTP y limpiar recursos
|
|
534
|
+
*
|
|
535
|
+
* Detiene el keep-alive y libera recursos del cliente HTTP.
|
|
536
|
+
* Debe llamarse cuando el cliente ya no se necesite.
|
|
537
|
+
*
|
|
538
|
+
* @since 1.0.0
|
|
539
|
+
*/
|
|
540
|
+
destroy(): void {
|
|
541
|
+
this.logger?.debug?.('Destroying ClientWithCreds')
|
|
542
|
+
this.auth.stopKeepAlive()
|
|
543
|
+
this.http.destroy()
|
|
544
|
+
}
|
|
545
|
+
}
|