|
|
|
|
@@ -64,13 +64,11 @@ export class CapacitorPlatformService
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Phase execution wrapper - logs once, throws on failure
|
|
|
|
|
* Phase execution wrapper - logs errors only
|
|
|
|
|
*/
|
|
|
|
|
private async runPhase<T>(name: string, fn: () => Promise<T>): Promise<T> {
|
|
|
|
|
try {
|
|
|
|
|
const out = await fn();
|
|
|
|
|
logger.log(`[CapacitorPlatformService] ${name} ok`);
|
|
|
|
|
return out;
|
|
|
|
|
return await fn();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.error(`[CapacitorPlatformService] ${name} failed`, e);
|
|
|
|
|
throw e;
|
|
|
|
|
@@ -99,21 +97,18 @@ export class CapacitorPlatformService
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply SQLite PRAGMAs - single vetted sequence
|
|
|
|
|
*
|
|
|
|
|
* Note: This service is only loaded when VITE_PLATFORM=capacitor, so we're
|
|
|
|
|
* always running on a native platform (iOS/Android/Electron). The Capacitor
|
|
|
|
|
* SQLite plugin requires native platform support.
|
|
|
|
|
*/
|
|
|
|
|
private async applyPragmas(db: SQLiteDBConnection): Promise<void> {
|
|
|
|
|
const platform = Capacitor.getPlatform();
|
|
|
|
|
if (platform !== "web") {
|
|
|
|
|
try {
|
|
|
|
|
await db.query("PRAGMA journal_mode = WAL;");
|
|
|
|
|
await db.query("PRAGMA synchronous = NORMAL;");
|
|
|
|
|
} catch {
|
|
|
|
|
// Non-fatal if WAL/synchronous fail
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
await db.query("PRAGMA journal_mode = WAL;");
|
|
|
|
|
await db.query("PRAGMA synchronous = NORMAL;");
|
|
|
|
|
await db.run("PRAGMA foreign_keys = ON;", []);
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore errors
|
|
|
|
|
// Non-fatal if any PRAGMA fails
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
await db.query("PRAGMA busy_timeout = 5000;");
|
|
|
|
|
@@ -464,12 +459,11 @@ export class CapacitorPlatformService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async processQueue(): Promise<void> {
|
|
|
|
|
if (!this.initialized || !this.db) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const executor: QueueExecutor<SQLiteDBConnection> = {
|
|
|
|
|
/**
|
|
|
|
|
* Create executor adapter for Capacitor SQLite API
|
|
|
|
|
*/
|
|
|
|
|
private createExecutor(): QueueExecutor<SQLiteDBConnection> {
|
|
|
|
|
return {
|
|
|
|
|
executeRun: async (db, sql, params) => {
|
|
|
|
|
const runResult = await db.run(sql, params);
|
|
|
|
|
return {
|
|
|
|
|
@@ -488,10 +482,16 @@ export class CapacitorPlatformService
|
|
|
|
|
return await db.query(sql, params);
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async processQueue(): Promise<void> {
|
|
|
|
|
if (!this.initialized || !this.db) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.operationQueue.processQueue(
|
|
|
|
|
this.db,
|
|
|
|
|
executor,
|
|
|
|
|
this.createExecutor(),
|
|
|
|
|
"CapacitorPlatformService",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@@ -501,11 +501,8 @@ export class CapacitorPlatformService
|
|
|
|
|
sql: string,
|
|
|
|
|
params: unknown[] = [],
|
|
|
|
|
): Promise<R> {
|
|
|
|
|
// Only log SQL operations in debug mode to reduce console noise
|
|
|
|
|
logger.debug(`[CapacitorPlatformService] queueOperation - SQL: ${sql}`);
|
|
|
|
|
|
|
|
|
|
// Convert parameters to SQLite-compatible types with robust serialization
|
|
|
|
|
const convertedParams = params.map((param, index) => {
|
|
|
|
|
const convertedParams = params.map((param) => {
|
|
|
|
|
if (param === null || param === undefined) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -519,9 +516,6 @@ export class CapacitorPlatformService
|
|
|
|
|
stringRep.includes("Proxy(") || stringRep.startsWith("Proxy");
|
|
|
|
|
|
|
|
|
|
if (isProxy || forceProxyDetection) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
`[CapacitorPlatformService] Proxy object detected at index ${index}`,
|
|
|
|
|
);
|
|
|
|
|
try {
|
|
|
|
|
// AGGRESSIVE EXTRACTION: Try multiple methods to extract actual values
|
|
|
|
|
if (Array.isArray(param)) {
|
|
|
|
|
@@ -534,11 +528,6 @@ export class CapacitorPlatformService
|
|
|
|
|
return actualObject;
|
|
|
|
|
}
|
|
|
|
|
} catch (proxyError) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
`[CapacitorPlatformService] Failed to extract from Proxy at index ${index}:`,
|
|
|
|
|
proxyError,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// FALLBACK: Try to extract primitive values manually
|
|
|
|
|
if (Array.isArray(param)) {
|
|
|
|
|
try {
|
|
|
|
|
@@ -560,11 +549,6 @@ export class CapacitorPlatformService
|
|
|
|
|
return JSON.stringify(param);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// Handle non-serializable objects
|
|
|
|
|
logger.debug(
|
|
|
|
|
`[CapacitorPlatformService] Failed to serialize parameter at index ${index}:`,
|
|
|
|
|
error,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Fallback: Convert to string representation
|
|
|
|
|
if (Array.isArray(param)) {
|
|
|
|
|
return `[Array(${param.length})]`;
|
|
|
|
|
@@ -578,16 +562,10 @@ export class CapacitorPlatformService
|
|
|
|
|
}
|
|
|
|
|
if (typeof param === "function") {
|
|
|
|
|
// Functions can't be serialized - convert to string representation
|
|
|
|
|
logger.debug(
|
|
|
|
|
`[CapacitorPlatformService] Function parameter detected and converted to string at index ${index}`,
|
|
|
|
|
);
|
|
|
|
|
return `[Function ${param.name || "Anonymous"}]`;
|
|
|
|
|
}
|
|
|
|
|
if (typeof param === "symbol") {
|
|
|
|
|
// Symbols can't be serialized - convert to string representation
|
|
|
|
|
logger.debug(
|
|
|
|
|
`[CapacitorPlatformService] Symbol parameter detected and converted to string at index ${index}`,
|
|
|
|
|
);
|
|
|
|
|
return param.toString();
|
|
|
|
|
}
|
|
|
|
|
// Numbers, strings, bigints are supported, but ensure bigints are converted to strings
|
|
|
|
|
@@ -711,28 +689,18 @@ export class CapacitorPlatformService
|
|
|
|
|
// Method 1: Check toString representation
|
|
|
|
|
const objString = obj.toString();
|
|
|
|
|
if (objString.includes("Proxy(") || objString.startsWith("Proxy")) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
"[CapacitorPlatformService] Proxy detected via toString:",
|
|
|
|
|
objString,
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Method 2: Check constructor name
|
|
|
|
|
const constructorName = obj.constructor?.name;
|
|
|
|
|
if (constructorName === "Proxy") {
|
|
|
|
|
logger.debug(
|
|
|
|
|
"[CapacitorPlatformService] Proxy detected via constructor name",
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Method 3: Check Object.prototype.toString
|
|
|
|
|
const objToString = Object.prototype.toString.call(obj);
|
|
|
|
|
if (objToString.includes("Proxy")) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
"[CapacitorPlatformService] Proxy detected via Object.prototype.toString",
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -743,9 +711,6 @@ export class CapacitorPlatformService
|
|
|
|
|
(prop) => prop.startsWith("__v_") || prop.startsWith("__r_"),
|
|
|
|
|
);
|
|
|
|
|
if (hasVueProxy) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
"[CapacitorPlatformService] Vue reactive Proxy detected",
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -754,9 +719,6 @@ export class CapacitorPlatformService
|
|
|
|
|
try {
|
|
|
|
|
const jsonString = JSON.stringify(obj);
|
|
|
|
|
if (jsonString.includes("Proxy")) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
"[CapacitorPlatformService] Proxy detected in JSON serialization",
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} catch (jsonError) {
|
|
|
|
|
@@ -768,9 +730,6 @@ export class CapacitorPlatformService
|
|
|
|
|
errorMessage.includes("circular") ||
|
|
|
|
|
errorMessage.includes("clone")
|
|
|
|
|
) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
"[CapacitorPlatformService] Proxy detected via JSON serialization error",
|
|
|
|
|
);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -858,8 +817,6 @@ export class CapacitorPlatformService
|
|
|
|
|
* @returns Promise resolving to execution results
|
|
|
|
|
*/
|
|
|
|
|
const sqlExec = async (sql: string, params?: unknown[]): Promise<void> => {
|
|
|
|
|
logger.debug(`🔧 [CapacitorMigration] Executing SQL:`, sql);
|
|
|
|
|
|
|
|
|
|
if (params && params.length > 0) {
|
|
|
|
|
// Use run method for parameterized queries (prepared statements)
|
|
|
|
|
// This is essential for proper parameter binding and SQL injection prevention
|
|
|
|
|
@@ -901,8 +858,6 @@ export class CapacitorPlatformService
|
|
|
|
|
sql: string,
|
|
|
|
|
params?: unknown[],
|
|
|
|
|
): Promise<DBSQLiteValues> => {
|
|
|
|
|
logger.debug(`🔍 [CapacitorMigration] Querying SQL:`, sql);
|
|
|
|
|
|
|
|
|
|
const result = await this.db!.query(sql, params);
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
@@ -926,11 +881,6 @@ export class CapacitorPlatformService
|
|
|
|
|
* @returns Set of migration names found in the result
|
|
|
|
|
*/
|
|
|
|
|
const extractMigrationNames = (result: DBSQLiteValues): Set<string> => {
|
|
|
|
|
logger.debug(
|
|
|
|
|
`🔍 [CapacitorMigration] Extracting migration names from:`,
|
|
|
|
|
result,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Handle the Capacitor SQLite result format
|
|
|
|
|
const names =
|
|
|
|
|
result.values
|
|
|
|
|
@@ -945,7 +895,6 @@ export class CapacitorPlatformService
|
|
|
|
|
})
|
|
|
|
|
.filter((name) => name !== null) || [];
|
|
|
|
|
|
|
|
|
|
logger.debug(`📋 [CapacitorMigration] Extracted names:`, names);
|
|
|
|
|
return new Set(names);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -956,7 +905,7 @@ export class CapacitorPlatformService
|
|
|
|
|
// After migrations, run integrity check to verify database state
|
|
|
|
|
await this.verifyDatabaseIntegrity();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`❌ [CapacitorMigration] Migration failed:`, error);
|
|
|
|
|
logger.error("[CapacitorPlatformService] Migration failed:", error);
|
|
|
|
|
// Still try to verify what we have for debugging purposes
|
|
|
|
|
await this.verifyDatabaseIntegrity();
|
|
|
|
|
throw error;
|
|
|
|
|
@@ -1017,18 +966,17 @@ export class CapacitorPlatformService
|
|
|
|
|
*/
|
|
|
|
|
private async verifyDatabaseIntegrity(): Promise<void> {
|
|
|
|
|
if (!this.db) {
|
|
|
|
|
logger.error(`❌ [DB-Integrity] Database not initialized`);
|
|
|
|
|
logger.error(
|
|
|
|
|
"[CapacitorPlatformService] Database not initialized for integrity check",
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.debug(`🔍 [DB-Integrity] Starting database integrity check...`);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Step 1: Check migrations table and applied migrations
|
|
|
|
|
const migrationsResult = await this.db.query(
|
|
|
|
|
await this.db.query(
|
|
|
|
|
"SELECT name, applied_at FROM migrations ORDER BY applied_at",
|
|
|
|
|
);
|
|
|
|
|
logger.debug(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
|
|
|
|
|
|
|
|
|
|
// Step 2: Verify core tables exist
|
|
|
|
|
const coreTableNames = [
|
|
|
|
|
@@ -1048,13 +996,14 @@ export class CapacitorPlatformService
|
|
|
|
|
);
|
|
|
|
|
if (tableCheck.values && tableCheck.values.length > 0) {
|
|
|
|
|
existingTables.push(tableName);
|
|
|
|
|
logger.debug(`✅ [DB-Integrity] Table ${tableName} exists`);
|
|
|
|
|
} else {
|
|
|
|
|
logger.error(`❌ [DB-Integrity] Table ${tableName} missing`);
|
|
|
|
|
logger.error(
|
|
|
|
|
`[CapacitorPlatformService] Table ${tableName} missing`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(
|
|
|
|
|
`❌ [DB-Integrity] Error checking table ${tableName}:`,
|
|
|
|
|
`[CapacitorPlatformService] Error checking table ${tableName}:`,
|
|
|
|
|
error,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@@ -1066,10 +1015,6 @@ export class CapacitorPlatformService
|
|
|
|
|
const contactsSchema = await this.db.query(
|
|
|
|
|
"PRAGMA table_info(contacts)",
|
|
|
|
|
);
|
|
|
|
|
logger.debug(
|
|
|
|
|
`📊 [DB-Integrity] Contacts table schema:`,
|
|
|
|
|
contactsSchema,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Check for iViewContent column specifically
|
|
|
|
|
const hasIViewContent = contactsSchema.values?.some(
|
|
|
|
|
@@ -1081,18 +1026,14 @@ export class CapacitorPlatformService
|
|
|
|
|
(Array.isArray(col) && col[1] === "iViewContent"),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (hasIViewContent) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
`✅ [DB-Integrity] iViewContent column exists in contacts table`,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
if (!hasIViewContent) {
|
|
|
|
|
logger.error(
|
|
|
|
|
`❌ [DB-Integrity] iViewContent column missing from contacts table`,
|
|
|
|
|
"[CapacitorPlatformService] iViewContent column missing from contacts table",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(
|
|
|
|
|
`❌ [DB-Integrity] Error checking contacts schema:`,
|
|
|
|
|
"[CapacitorPlatformService] Error checking contacts schema:",
|
|
|
|
|
error,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@@ -1100,26 +1041,20 @@ export class CapacitorPlatformService
|
|
|
|
|
|
|
|
|
|
// Step 4: Check for basic data integrity
|
|
|
|
|
try {
|
|
|
|
|
const accountCount = await this.db.query(
|
|
|
|
|
"SELECT COUNT(*) as count FROM accounts",
|
|
|
|
|
);
|
|
|
|
|
const settingsCount = await this.db.query(
|
|
|
|
|
"SELECT COUNT(*) as count FROM settings",
|
|
|
|
|
);
|
|
|
|
|
const contactsCount = await this.db.query(
|
|
|
|
|
"SELECT COUNT(*) as count FROM contacts",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
logger.debug(
|
|
|
|
|
`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`,
|
|
|
|
|
);
|
|
|
|
|
await this.db.query("SELECT COUNT(*) as count FROM accounts");
|
|
|
|
|
await this.db.query("SELECT COUNT(*) as count FROM settings");
|
|
|
|
|
await this.db.query("SELECT COUNT(*) as count FROM contacts");
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`❌ [DB-Integrity] Error checking data counts:`, error);
|
|
|
|
|
logger.error(
|
|
|
|
|
"[CapacitorPlatformService] Error checking data counts:",
|
|
|
|
|
error,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.log(`✅ [DB-Integrity] Database integrity check completed`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
|
|
|
|
|
logger.error(
|
|
|
|
|
"[CapacitorPlatformService] Database integrity check failed:",
|
|
|
|
|
error,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1148,15 +1083,6 @@ export class CapacitorPlatformService
|
|
|
|
|
*/
|
|
|
|
|
private async checkStoragePermissions(): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
const logData = {
|
|
|
|
|
platform: this.getCapabilities().isIOS ? "iOS" : "Android",
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.log(
|
|
|
|
|
"Checking storage permissions",
|
|
|
|
|
JSON.stringify(logData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (this.getCapabilities().isIOS) {
|
|
|
|
|
// iOS uses different permission model
|
|
|
|
|
return;
|
|
|
|
|
@@ -1168,28 +1094,12 @@ export class CapacitorPlatformService
|
|
|
|
|
path: "/storage/emulated/0/Download",
|
|
|
|
|
directory: Directory.Documents,
|
|
|
|
|
});
|
|
|
|
|
logger.log(
|
|
|
|
|
"Storage permissions already granted",
|
|
|
|
|
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
const err = error as Error;
|
|
|
|
|
const errorLogData = {
|
|
|
|
|
error: {
|
|
|
|
|
message: err.message,
|
|
|
|
|
name: err.name,
|
|
|
|
|
stack: err.stack,
|
|
|
|
|
},
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// "File does not exist" is expected and not a permission error
|
|
|
|
|
if (err.message === "File does not exist") {
|
|
|
|
|
logger.log(
|
|
|
|
|
"Directory does not exist (expected), proceeding with write",
|
|
|
|
|
JSON.stringify(errorLogData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1198,11 +1108,6 @@ export class CapacitorPlatformService
|
|
|
|
|
err.message.includes("permission") ||
|
|
|
|
|
err.message.includes("access")
|
|
|
|
|
) {
|
|
|
|
|
logger.log(
|
|
|
|
|
"Permission check failed, requesting permissions",
|
|
|
|
|
JSON.stringify(errorLogData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// The Filesystem plugin will automatically request permissions when needed
|
|
|
|
|
// We just need to try the operation again
|
|
|
|
|
try {
|
|
|
|
|
@@ -1210,10 +1115,6 @@ export class CapacitorPlatformService
|
|
|
|
|
path: "/storage/emulated/0/Download",
|
|
|
|
|
directory: Directory.Documents,
|
|
|
|
|
});
|
|
|
|
|
logger.log(
|
|
|
|
|
"Storage permissions granted after request",
|
|
|
|
|
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
} catch (retryError: unknown) {
|
|
|
|
|
const retryErr = retryError as Error;
|
|
|
|
|
@@ -1223,26 +1124,14 @@ export class CapacitorPlatformService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For any other error, log it but don't treat as permission error
|
|
|
|
|
logger.log(
|
|
|
|
|
"Unexpected error during permission check",
|
|
|
|
|
JSON.stringify(errorLogData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
// For any other error, ignore it (not a permission error)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
const err = error as Error;
|
|
|
|
|
const errorLogData = {
|
|
|
|
|
error: {
|
|
|
|
|
message: err.message,
|
|
|
|
|
name: err.name,
|
|
|
|
|
stack: err.stack,
|
|
|
|
|
},
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.error(
|
|
|
|
|
"Error checking/requesting permissions",
|
|
|
|
|
JSON.stringify(errorLogData, null, 2),
|
|
|
|
|
"[CapacitorPlatformService] Error checking/requesting permissions:",
|
|
|
|
|
err,
|
|
|
|
|
);
|
|
|
|
|
throw new Error(`Failed to obtain storage permissions: ${err.message}`);
|
|
|
|
|
}
|
|
|
|
|
@@ -1298,17 +1187,6 @@ export class CapacitorPlatformService
|
|
|
|
|
// Check storage permissions before proceeding
|
|
|
|
|
await this.checkStoragePermissions();
|
|
|
|
|
|
|
|
|
|
const logData = {
|
|
|
|
|
targetFileName: fileName,
|
|
|
|
|
contentLength: content.length,
|
|
|
|
|
platform: this.getCapabilities().isIOS ? "iOS" : "Android",
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.log(
|
|
|
|
|
"Starting writeFile operation",
|
|
|
|
|
JSON.stringify(logData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// For Android, we need to handle content URIs differently
|
|
|
|
|
if (this.getCapabilities().isIOS) {
|
|
|
|
|
// Write to app's Documents directory for iOS
|
|
|
|
|
@@ -1319,15 +1197,6 @@ export class CapacitorPlatformService
|
|
|
|
|
encoding: Encoding.UTF8,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const writeSuccessLogData = {
|
|
|
|
|
path: writeResult.uri,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.log(
|
|
|
|
|
"File write successful",
|
|
|
|
|
JSON.stringify(writeSuccessLogData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Offer to share the file
|
|
|
|
|
try {
|
|
|
|
|
await Share.share({
|
|
|
|
|
@@ -1336,23 +1205,11 @@ export class CapacitorPlatformService
|
|
|
|
|
url: writeResult.uri,
|
|
|
|
|
dialogTitle: "Share your backup",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.log(
|
|
|
|
|
"Share dialog shown",
|
|
|
|
|
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
|
|
|
|
|
);
|
|
|
|
|
} catch (shareError) {
|
|
|
|
|
// Log share error but don't fail the operation
|
|
|
|
|
logger.error(
|
|
|
|
|
"Share dialog failed",
|
|
|
|
|
JSON.stringify(
|
|
|
|
|
{
|
|
|
|
|
error: shareError,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
},
|
|
|
|
|
null,
|
|
|
|
|
2,
|
|
|
|
|
),
|
|
|
|
|
"[CapacitorPlatformService] Share dialog failed:",
|
|
|
|
|
shareError,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
@@ -1364,15 +1221,6 @@ export class CapacitorPlatformService
|
|
|
|
|
encoding: Encoding.UTF8,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const writeSuccessLogData = {
|
|
|
|
|
path: writeResult.uri,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.log(
|
|
|
|
|
"File write successful to app storage",
|
|
|
|
|
JSON.stringify(writeSuccessLogData, null, 2),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Then share the file to let user choose where to save it
|
|
|
|
|
try {
|
|
|
|
|
await Share.share({
|
|
|
|
|
@@ -1381,39 +1229,19 @@ export class CapacitorPlatformService
|
|
|
|
|
url: writeResult.uri,
|
|
|
|
|
dialogTitle: "Save your backup",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.log(
|
|
|
|
|
"Share dialog shown for Android",
|
|
|
|
|
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
|
|
|
|
|
);
|
|
|
|
|
} catch (shareError) {
|
|
|
|
|
// Log share error but don't fail the operation
|
|
|
|
|
logger.error(
|
|
|
|
|
"Share dialog failed for Android",
|
|
|
|
|
JSON.stringify(
|
|
|
|
|
{
|
|
|
|
|
error: shareError,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
},
|
|
|
|
|
null,
|
|
|
|
|
2,
|
|
|
|
|
),
|
|
|
|
|
"[CapacitorPlatformService] Share dialog failed:",
|
|
|
|
|
shareError,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
const err = error as Error;
|
|
|
|
|
const finalErrorLogData = {
|
|
|
|
|
error: {
|
|
|
|
|
message: err.message,
|
|
|
|
|
name: err.name,
|
|
|
|
|
stack: err.stack,
|
|
|
|
|
},
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.error(
|
|
|
|
|
"Error in writeFile operation:",
|
|
|
|
|
JSON.stringify(finalErrorLogData, null, 2),
|
|
|
|
|
"[CapacitorPlatformService] Error in writeFile operation:",
|
|
|
|
|
err,
|
|
|
|
|
);
|
|
|
|
|
throw new Error(`Failed to save file: ${err.message}`);
|
|
|
|
|
}
|
|
|
|
|
@@ -1429,15 +1257,6 @@ export class CapacitorPlatformService
|
|
|
|
|
* @param content - The content to write to the file
|
|
|
|
|
*/
|
|
|
|
|
async writeAndShareFile(fileName: string, content: string): Promise<void> {
|
|
|
|
|
const timestamp = new Date().toISOString();
|
|
|
|
|
const logData = {
|
|
|
|
|
action: "writeAndShareFile",
|
|
|
|
|
fileName,
|
|
|
|
|
contentLength: content.length,
|
|
|
|
|
timestamp,
|
|
|
|
|
};
|
|
|
|
|
logger.log("[CapacitorPlatformService]", JSON.stringify(logData, null, 2));
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Check storage permissions before proceeding
|
|
|
|
|
await this.checkStoragePermissions();
|
|
|
|
|
@@ -1450,11 +1269,6 @@ export class CapacitorPlatformService
|
|
|
|
|
recursive: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] File write successful:", {
|
|
|
|
|
uri,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Share.share({
|
|
|
|
|
title: "TimeSafari Backup",
|
|
|
|
|
text: "Here is your backup file.",
|
|
|
|
|
@@ -1463,14 +1277,9 @@ export class CapacitorPlatformService
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const err = error as Error;
|
|
|
|
|
const errLog = {
|
|
|
|
|
message: err.message,
|
|
|
|
|
stack: err.stack,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
logger.error(
|
|
|
|
|
"[CapacitorPlatformService] Error writing or sharing file:",
|
|
|
|
|
JSON.stringify(errLog, null, 2),
|
|
|
|
|
err,
|
|
|
|
|
);
|
|
|
|
|
throw new Error(`Failed to write or share file: ${err.message}`);
|
|
|
|
|
}
|
|
|
|
|
@@ -1592,7 +1401,6 @@ export class CapacitorPlatformService
|
|
|
|
|
this.currentDirection === CameraDirection.Rear
|
|
|
|
|
? CameraDirection.Front
|
|
|
|
|
: CameraDirection.Rear;
|
|
|
|
|
logger.debug(`Camera rotated to ${this.currentDirection} camera`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|