appwrite-utils-cli 1.6.3 → 1.6.4
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/CONFIG_TODO.md +1189 -0
- package/SERVICE_IMPLEMENTATION_REPORT.md +462 -0
- package/dist/cli/commands/configCommands.js +7 -1
- package/dist/collections/attributes.js +102 -30
- package/dist/config/ConfigManager.d.ts +445 -0
- package/dist/config/ConfigManager.js +625 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.js +7 -0
- package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
- package/dist/config/services/ConfigDiscoveryService.js +374 -0
- package/dist/config/services/ConfigLoaderService.d.ts +105 -0
- package/dist/config/services/ConfigLoaderService.js +410 -0
- package/dist/config/services/ConfigMergeService.d.ts +208 -0
- package/dist/config/services/ConfigMergeService.js +307 -0
- package/dist/config/services/ConfigValidationService.d.ts +214 -0
- package/dist/config/services/ConfigValidationService.js +310 -0
- package/dist/config/services/SessionAuthService.d.ts +225 -0
- package/dist/config/services/SessionAuthService.js +456 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
- package/dist/config/services/index.d.ts +13 -0
- package/dist/config/services/index.js +10 -0
- package/dist/interactiveCLI.js +8 -6
- package/dist/main.js +2 -2
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +1 -1
- package/dist/shared/operationQueue.js +1 -1
- package/dist/utils/ClientFactory.d.ts +87 -0
- package/dist/utils/ClientFactory.js +164 -0
- package/dist/utils/getClientFromConfig.js +4 -3
- package/dist/utils/helperFunctions.d.ts +1 -0
- package/dist/utils/helperFunctions.js +21 -5
- package/dist/utils/yamlConverter.d.ts +2 -2
- package/dist/utils/yamlConverter.js +2 -2
- package/dist/utilsController.d.ts +18 -15
- package/dist/utilsController.js +83 -131
- package/package.json +1 -1
- package/src/cli/commands/configCommands.ts +8 -1
- package/src/collections/attributes.ts +118 -31
- package/src/config/ConfigManager.ts +808 -0
- package/src/config/index.ts +10 -0
- package/src/config/services/ConfigDiscoveryService.ts +463 -0
- package/src/config/services/ConfigLoaderService.ts +560 -0
- package/src/config/services/ConfigMergeService.ts +386 -0
- package/src/config/services/ConfigValidationService.ts +394 -0
- package/src/config/services/SessionAuthService.ts +565 -0
- package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
- package/src/config/services/index.ts +29 -0
- package/src/interactiveCLI.ts +9 -7
- package/src/main.ts +2 -2
- package/src/shared/operationQueue.ts +1 -1
- package/src/utils/ClientFactory.ts +186 -0
- package/src/utils/getClientFromConfig.ts +4 -3
- package/src/utils/helperFunctions.ts +27 -7
- package/src/utils/yamlConverter.ts +4 -4
- package/src/utilsController.ts +99 -187
@@ -135,6 +135,7 @@ export let numTimesFailedTotal = 0;
|
|
135
135
|
|
136
136
|
/**
|
137
137
|
* Tries to execute the given createFunction and retries up to 5 times if it fails.
|
138
|
+
* Only retries on transient errors (network failures, 5xx errors). Does NOT retry validation errors (4xx).
|
138
139
|
*
|
139
140
|
* @param {() => Promise<any>} createFunction - The function to be executed.
|
140
141
|
* @param {number} [attemptNum=0] - The number of attempts made so far (default: 0).
|
@@ -148,13 +149,30 @@ export const tryAwaitWithRetry = async <T>(
|
|
148
149
|
try {
|
149
150
|
return await createFunction();
|
150
151
|
} catch (error) {
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
152
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
153
|
+
const errorCode = (error as any).code;
|
154
|
+
|
155
|
+
// Check if this is a validation error that should NOT be retried
|
156
|
+
const isValidationError =
|
157
|
+
errorCode === 400 || errorCode === 409 || errorCode === 422 ||
|
158
|
+
errorMessage.includes("already exists") ||
|
159
|
+
errorMessage.includes("attribute with the same key") ||
|
160
|
+
errorMessage.includes("invalid") && !errorMessage.includes("fetch failed") ||
|
161
|
+
errorMessage.includes("conflict") ||
|
162
|
+
errorMessage.includes("bad request");
|
163
|
+
|
164
|
+
// Check if this is a transient error that SHOULD be retried
|
165
|
+
const isTransientError =
|
166
|
+
errorCode === 522 || errorCode === "522" || // Cloudflare error
|
167
|
+
errorCode >= 500 && errorCode < 600 || // 5xx server errors
|
168
|
+
errorMessage.includes("fetch failed") || // Network failures
|
169
|
+
errorMessage.includes("timeout") ||
|
170
|
+
errorMessage.includes("econnrefused") ||
|
171
|
+
errorMessage.includes("network error");
|
172
|
+
|
173
|
+
// Only retry if it's a transient error AND not a validation error
|
174
|
+
if (isTransientError && !isValidationError) {
|
175
|
+
if (errorCode === 522 || errorCode === "522") {
|
158
176
|
console.log("Cloudflare error. Retrying...");
|
159
177
|
} else {
|
160
178
|
console.log(`Fetch failed on attempt ${attemptNum}. Retrying...`);
|
@@ -166,6 +184,8 @@ export const tryAwaitWithRetry = async <T>(
|
|
166
184
|
await delay(2500);
|
167
185
|
return tryAwaitWithRetry(createFunction, attemptNum + 1);
|
168
186
|
}
|
187
|
+
|
188
|
+
// For validation errors or non-transient errors, throw immediately
|
169
189
|
if (throwError) {
|
170
190
|
throw error;
|
171
191
|
}
|
@@ -19,7 +19,7 @@ export interface YamlCollectionData {
|
|
19
19
|
size?: number;
|
20
20
|
required?: boolean;
|
21
21
|
array?: boolean;
|
22
|
-
|
22
|
+
encrypted?: boolean;
|
23
23
|
default?: any;
|
24
24
|
min?: number;
|
25
25
|
max?: number;
|
@@ -38,7 +38,7 @@ export interface YamlCollectionData {
|
|
38
38
|
size?: number;
|
39
39
|
required?: boolean;
|
40
40
|
array?: boolean;
|
41
|
-
|
41
|
+
encrypted?: boolean;
|
42
42
|
default?: any;
|
43
43
|
min?: number;
|
44
44
|
max?: number;
|
@@ -111,9 +111,9 @@ export function collectionToYaml(
|
|
111
111
|
if (attr.required !== undefined) yamlAttr.required = attr.required;
|
112
112
|
if (attr.array !== undefined) yamlAttr.array = attr.array;
|
113
113
|
|
114
|
-
// Always include
|
114
|
+
// Always include encrypted field for string attributes (default to false)
|
115
115
|
if (attr.type === 'string') {
|
116
|
-
yamlAttr.
|
116
|
+
yamlAttr.encrypted = ('encrypted' in attr && attr.encrypted === true) ? true : false;
|
117
117
|
}
|
118
118
|
|
119
119
|
if ('xdefault' in attr && attr.xdefault !== undefined) yamlAttr.default = attr.xdefault;
|
package/src/utilsController.ts
CHANGED
@@ -60,7 +60,6 @@ import { getClient, getClientWithAuth } from "./utils/getClientFromConfig.js";
|
|
60
60
|
import { getAdapterFromConfig } from "./utils/getClientFromConfig.js";
|
61
61
|
import type { DatabaseAdapter } from './adapters/DatabaseAdapter.js';
|
62
62
|
import { hasSessionAuth, findSessionByEndpointAndProject, isValidSessionCookie, type SessionAuthInfo } from "./utils/sessionAuth.js";
|
63
|
-
import { createSessionPreservation, type SessionPreservationOptions } from "./utils/loadConfigs.js";
|
64
63
|
import { fetchAllDatabases } from "./databases/methods.js";
|
65
64
|
import {
|
66
65
|
listFunctions,
|
@@ -69,7 +68,7 @@ import {
|
|
69
68
|
import chalk from "chalk";
|
70
69
|
import { deployLocalFunction } from "./functions/deployments.js";
|
71
70
|
import fs from "node:fs";
|
72
|
-
import { configureLogging, updateLogger } from "./shared/logging.js";
|
71
|
+
import { configureLogging, updateLogger, logger } from "./shared/logging.js";
|
73
72
|
import { MessageFormatter, Messages } from "./shared/messageFormatter.js";
|
74
73
|
import { SchemaGenerator } from "./shared/schemaGenerator.js";
|
75
74
|
import { findYamlConfig } from "./config/yamlConfig.js";
|
@@ -79,6 +78,8 @@ import {
|
|
79
78
|
validateWithStrictMode,
|
80
79
|
type ValidationResult
|
81
80
|
} from "./config/configValidation.js";
|
81
|
+
import { ConfigManager } from "./config/ConfigManager.js";
|
82
|
+
import { ClientFactory } from "./utils/ClientFactory.js";
|
82
83
|
|
83
84
|
export interface SetupOptions {
|
84
85
|
databases?: Models.Database[];
|
@@ -96,8 +97,42 @@ export interface SetupOptions {
|
|
96
97
|
}
|
97
98
|
|
98
99
|
export class UtilsController {
|
100
|
+
// ──────────────────────────────────────────────────
|
101
|
+
// SINGLETON PATTERN
|
102
|
+
// ──────────────────────────────────────────────────
|
103
|
+
private static instance: UtilsController | null = null;
|
104
|
+
private isInitialized: boolean = false;
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Get the UtilsController singleton instance
|
108
|
+
*/
|
109
|
+
public static getInstance(
|
110
|
+
currentUserDir: string,
|
111
|
+
directConfig?: {
|
112
|
+
appwriteEndpoint?: string;
|
113
|
+
appwriteProject?: string;
|
114
|
+
appwriteKey?: string;
|
115
|
+
}
|
116
|
+
): UtilsController {
|
117
|
+
if (!UtilsController.instance) {
|
118
|
+
UtilsController.instance = new UtilsController(currentUserDir, directConfig);
|
119
|
+
}
|
120
|
+
return UtilsController.instance;
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Clear the singleton instance (useful for testing)
|
125
|
+
*/
|
126
|
+
public static clearInstance(): void {
|
127
|
+
UtilsController.instance = null;
|
128
|
+
}
|
129
|
+
|
130
|
+
// ──────────────────────────────────────────────────
|
131
|
+
// INSTANCE FIELDS
|
132
|
+
// ──────────────────────────────────────────────────
|
99
133
|
private appwriteFolderPath?: string;
|
100
134
|
private appwriteConfigPath?: string;
|
135
|
+
private currentUserDir: string;
|
101
136
|
public config?: AppwriteConfig;
|
102
137
|
public appwriteServer?: Client;
|
103
138
|
public database?: Databases;
|
@@ -107,14 +142,6 @@ export class UtilsController {
|
|
107
142
|
public validityRuleDefinitions: ValidationRules = validationRules;
|
108
143
|
public afterImportActionsDefinitions: AfterImportActions = afterImportActions;
|
109
144
|
|
110
|
-
// Session preservation fields
|
111
|
-
private sessionCookie?: string;
|
112
|
-
private authMethod?: "session" | "apikey" | "auto";
|
113
|
-
private sessionMetadata?: {
|
114
|
-
email?: string;
|
115
|
-
expiresAt?: string;
|
116
|
-
};
|
117
|
-
|
118
145
|
constructor(
|
119
146
|
currentUserDir: string,
|
120
147
|
directConfig?: {
|
@@ -123,6 +150,7 @@ export class UtilsController {
|
|
123
150
|
appwriteKey?: string;
|
124
151
|
}
|
125
152
|
) {
|
153
|
+
this.currentUserDir = currentUserDir;
|
126
154
|
const basePath = currentUserDir;
|
127
155
|
|
128
156
|
if (directConfig) {
|
@@ -194,136 +222,69 @@ export class UtilsController {
|
|
194
222
|
}
|
195
223
|
|
196
224
|
async init(options: { validate?: boolean; strictMode?: boolean; useSession?: boolean; sessionCookie?: string } = {}) {
|
197
|
-
const { validate = false, strictMode = false
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
this.config = config;
|
208
|
-
MessageFormatter.info(`Loaded config from: ${actualConfigPath}`, { prefix: "Config" });
|
209
|
-
|
210
|
-
// Report validation results if validation was requested
|
211
|
-
if (validation && validate) {
|
212
|
-
reportValidationResults(validation, { verbose: false });
|
213
|
-
|
214
|
-
// In strict mode, throw if validation fails
|
215
|
-
if (strictMode && !validation.isValid) {
|
216
|
-
throw new Error(`Configuration validation failed in strict mode. Found ${validation.errors.length} validation errors.`);
|
217
|
-
}
|
218
|
-
}
|
219
|
-
} catch (error) {
|
220
|
-
MessageFormatter.error("Failed to load config from file", error instanceof Error ? error : undefined, { prefix: "Config" });
|
221
|
-
return;
|
222
|
-
}
|
223
|
-
} else {
|
224
|
-
MessageFormatter.error("No configuration available", undefined, { prefix: "Config" });
|
225
|
-
return;
|
226
|
-
}
|
225
|
+
const { validate = false, strictMode = false } = options;
|
226
|
+
const configManager = ConfigManager.getInstance();
|
227
|
+
|
228
|
+
// Load config if not already loaded
|
229
|
+
if (!configManager.hasConfig()) {
|
230
|
+
await configManager.loadConfig({
|
231
|
+
configDir: this.currentUserDir,
|
232
|
+
validate,
|
233
|
+
strictMode,
|
234
|
+
});
|
227
235
|
}
|
228
236
|
|
237
|
+
const config = configManager.getConfig();
|
238
|
+
|
229
239
|
// Configure logging based on config
|
230
|
-
if (
|
231
|
-
configureLogging(
|
240
|
+
if (config.logging) {
|
241
|
+
configureLogging(config.logging);
|
232
242
|
updateLogger();
|
233
243
|
}
|
234
244
|
|
235
|
-
//
|
236
|
-
|
237
|
-
const clientSessionCookie = sessionCookie || this.sessionCookie;
|
238
|
-
this.appwriteServer = getClientWithAuth(
|
239
|
-
this.config.appwriteEndpoint,
|
240
|
-
this.config.appwriteProject,
|
241
|
-
this.config.appwriteKey || undefined,
|
242
|
-
clientSessionCookie
|
243
|
-
);
|
245
|
+
// Create client and adapter (session already in config from ConfigManager)
|
246
|
+
const { client, adapter } = await ClientFactory.createFromConfig(config);
|
244
247
|
|
248
|
+
this.appwriteServer = client;
|
249
|
+
this.adapter = adapter;
|
250
|
+
this.config = config;
|
245
251
|
this.database = new Databases(this.appwriteServer);
|
246
252
|
this.storage = new Storage(this.appwriteServer);
|
247
253
|
this.config.appwriteClient = this.appwriteServer;
|
248
254
|
|
249
|
-
//
|
250
|
-
|
251
|
-
const
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
);
|
256
|
-
this.adapter = adapter;
|
257
|
-
|
258
|
-
MessageFormatter.info(`Database adapter initialized (apiMode: ${apiMode})`, {
|
259
|
-
prefix: "Adapter"
|
260
|
-
});
|
261
|
-
} catch (error) {
|
262
|
-
MessageFormatter.warning(
|
263
|
-
'Database adapter initialization failed - some features may not work',
|
264
|
-
{ prefix: "Adapter" }
|
265
|
-
);
|
255
|
+
// Log only on FIRST initialization to avoid spam
|
256
|
+
if (!this.isInitialized) {
|
257
|
+
const apiMode = adapter.getApiMode();
|
258
|
+
MessageFormatter.info(`Database adapter initialized (apiMode: ${apiMode})`, { prefix: "Adapter" });
|
259
|
+
this.isInitialized = true;
|
260
|
+
} else {
|
261
|
+
logger.debug("Adapter reused from cache", { prefix: "UtilsController" });
|
266
262
|
}
|
267
|
-
|
268
|
-
// Extract and store session information after successful authentication
|
269
|
-
this.extractSessionInfo();
|
270
263
|
}
|
271
264
|
|
272
265
|
async reloadConfig() {
|
273
|
-
|
274
|
-
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
275
|
-
return;
|
276
|
-
}
|
277
|
-
|
278
|
-
// Preserve session authentication during config reload
|
279
|
-
const preserveAuth = this.createSessionPreservationOptions();
|
266
|
+
const configManager = ConfigManager.getInstance();
|
280
267
|
|
281
|
-
|
282
|
-
|
283
|
-
});
|
284
|
-
if (!this.config) {
|
285
|
-
MessageFormatter.error("Failed to load config", undefined, { prefix: "Controller" });
|
286
|
-
return;
|
287
|
-
}
|
268
|
+
// Session preservation is automatic in ConfigManager
|
269
|
+
const config = await configManager.reloadConfig();
|
288
270
|
|
289
271
|
// Configure logging based on updated config
|
290
|
-
if (
|
291
|
-
configureLogging(
|
272
|
+
if (config.logging) {
|
273
|
+
configureLogging(config.logging);
|
292
274
|
updateLogger();
|
293
275
|
}
|
294
276
|
|
295
|
-
//
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
);
|
277
|
+
// Recreate client and adapter
|
278
|
+
const { client, adapter } = await ClientFactory.createFromConfig(config);
|
279
|
+
|
280
|
+
this.appwriteServer = client;
|
281
|
+
this.adapter = adapter;
|
282
|
+
this.config = config;
|
302
283
|
this.database = new Databases(this.appwriteServer);
|
303
284
|
this.storage = new Storage(this.appwriteServer);
|
304
285
|
this.config.appwriteClient = this.appwriteServer;
|
305
286
|
|
306
|
-
|
307
|
-
try {
|
308
|
-
const { adapter, apiMode } = await getAdapterFromConfig(
|
309
|
-
this.config,
|
310
|
-
false,
|
311
|
-
this.sessionCookie
|
312
|
-
);
|
313
|
-
this.adapter = adapter;
|
314
|
-
|
315
|
-
MessageFormatter.info(`Database adapter re-initialized (apiMode: ${apiMode})`, {
|
316
|
-
prefix: "Adapter"
|
317
|
-
});
|
318
|
-
} catch (error) {
|
319
|
-
MessageFormatter.warning(
|
320
|
-
'Database adapter re-initialization failed - some features may not work',
|
321
|
-
{ prefix: "Adapter" }
|
322
|
-
);
|
323
|
-
}
|
324
|
-
|
325
|
-
// Re-extract session information after reload
|
326
|
-
this.extractSessionInfo();
|
287
|
+
logger.debug("Config reloaded, adapter refreshed", { prefix: "UtilsController" });
|
327
288
|
}
|
328
289
|
|
329
290
|
|
@@ -510,7 +471,8 @@ export class UtilsController {
|
|
510
471
|
await this.init();
|
511
472
|
if (!this.database || !this.config) throw new Error("Database not initialized");
|
512
473
|
try {
|
513
|
-
|
474
|
+
// Session is already in config from ConfigManager
|
475
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false);
|
514
476
|
if (apiMode === 'tablesdb') {
|
515
477
|
await wipeAllTables(adapter, database.$id);
|
516
478
|
} else {
|
@@ -557,7 +519,8 @@ export class UtilsController {
|
|
557
519
|
await this.init();
|
558
520
|
if (!this.database || !this.config) throw new Error("Database not initialized");
|
559
521
|
try {
|
560
|
-
|
522
|
+
// Session is already in config from ConfigManager
|
523
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false);
|
561
524
|
if (apiMode === 'tablesdb') {
|
562
525
|
await wipeTableRows(adapter, database.$id, collection.$id);
|
563
526
|
} else {
|
@@ -915,82 +878,31 @@ export class UtilsController {
|
|
915
878
|
return validation;
|
916
879
|
}
|
917
880
|
|
918
|
-
/**
|
919
|
-
* Extract session information from the current authenticated client
|
920
|
-
* This preserves session context for use across config reloads and adapter operations
|
921
|
-
*/
|
922
|
-
private extractSessionInfo(): void {
|
923
|
-
if (!this.config) {
|
924
|
-
return;
|
925
|
-
}
|
926
|
-
|
927
|
-
// Try to extract session from current config first
|
928
|
-
if (this.config.sessionCookie && isValidSessionCookie(this.config.sessionCookie)) {
|
929
|
-
this.sessionCookie = this.config.sessionCookie;
|
930
|
-
this.authMethod = "session";
|
931
|
-
this.sessionMetadata = this.config.sessionMetadata;
|
932
|
-
MessageFormatter.debug("Extracted session from config", { prefix: "Session" });
|
933
|
-
return;
|
934
|
-
}
|
935
|
-
|
936
|
-
// Fall back to finding session from Appwrite CLI preferences
|
937
|
-
const sessionAuth = findSessionByEndpointAndProject(
|
938
|
-
this.config.appwriteEndpoint,
|
939
|
-
this.config.appwriteProject
|
940
|
-
);
|
941
|
-
|
942
|
-
if (sessionAuth && isValidSessionCookie(sessionAuth.sessionCookie)) {
|
943
|
-
this.sessionCookie = sessionAuth.sessionCookie;
|
944
|
-
this.authMethod = "session";
|
945
|
-
this.sessionMetadata = {
|
946
|
-
email: sessionAuth.email
|
947
|
-
};
|
948
|
-
MessageFormatter.debug(
|
949
|
-
`Extracted session from CLI preferences for ${sessionAuth.email || 'unknown user'}`,
|
950
|
-
{ prefix: "Session" }
|
951
|
-
);
|
952
|
-
return;
|
953
|
-
}
|
954
|
-
|
955
|
-
// No session found, using API key authentication
|
956
|
-
if (this.config.appwriteKey) {
|
957
|
-
this.authMethod = "apikey";
|
958
|
-
this.sessionCookie = undefined;
|
959
|
-
this.sessionMetadata = undefined;
|
960
|
-
MessageFormatter.debug("Using API key authentication", { prefix: "Session" });
|
961
|
-
}
|
962
|
-
}
|
963
|
-
|
964
|
-
/**
|
965
|
-
* Create session preservation options for passing to loadConfig
|
966
|
-
* Returns current session state to maintain authentication across config reloads
|
967
|
-
*/
|
968
|
-
private createSessionPreservationOptions(): SessionPreservationOptions | undefined {
|
969
|
-
if (!this.sessionCookie || !isValidSessionCookie(this.sessionCookie)) {
|
970
|
-
return undefined;
|
971
|
-
}
|
972
|
-
|
973
|
-
return createSessionPreservation(
|
974
|
-
this.sessionCookie,
|
975
|
-
this.sessionMetadata?.email,
|
976
|
-
this.sessionMetadata?.expiresAt
|
977
|
-
);
|
978
|
-
}
|
979
|
-
|
980
881
|
/**
|
981
882
|
* Get current session information for debugging/logging purposes
|
883
|
+
* Delegates to ConfigManager for session info
|
982
884
|
*/
|
983
|
-
public getSessionInfo(): {
|
885
|
+
public async getSessionInfo(): Promise<{
|
984
886
|
hasSession: boolean;
|
985
887
|
authMethod?: string;
|
986
888
|
email?: string;
|
987
889
|
expiresAt?: string;
|
988
|
-
} {
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
890
|
+
}> {
|
891
|
+
const configManager = ConfigManager.getInstance();
|
892
|
+
|
893
|
+
try {
|
894
|
+
const authStatus = await configManager.getAuthStatus();
|
895
|
+
return {
|
896
|
+
hasSession: authStatus.hasValidSession,
|
897
|
+
authMethod: authStatus.authMethod,
|
898
|
+
email: authStatus.sessionInfo?.email,
|
899
|
+
expiresAt: authStatus.sessionInfo?.expiresAt
|
900
|
+
};
|
901
|
+
} catch (error) {
|
902
|
+
// If config not loaded, return empty status
|
903
|
+
return {
|
904
|
+
hasSession: false
|
905
|
+
};
|
906
|
+
}
|
995
907
|
}
|
996
908
|
}
|