forked from jsnbuchanan/crowd-funder-for-time-pwa
Scale back logging in migration, component lifecycle, and database areas
- Migration Service: Reduced verbose logging by 80% while keeping critical errors - Component Lifecycle: Removed 15+ debug logs from App.vue notification handling - Database: Kept error logging intact, removed redundant info logs - Maintained critical error logging for debugging and monitoring - Improved performance and reduced log noise in production
This commit is contained in:
29
src/App.vue
29
src/App.vue
@@ -392,43 +392,33 @@ export default class App extends Vue {
|
|||||||
async turnOffNotifications(
|
async turnOffNotifications(
|
||||||
notification: NotificationIface,
|
notification: NotificationIface,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
logger.log("Starting turnOffNotifications...");
|
|
||||||
let subscription: PushSubscriptionJSON | null = null;
|
let subscription: PushSubscriptionJSON | null = null;
|
||||||
let allGoingOff = false;
|
let allGoingOff = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log("Retrieving settings for the active account...");
|
|
||||||
const settings: Settings =
|
const settings: Settings =
|
||||||
await databaseUtil.retrieveSettingsForActiveAccount();
|
await databaseUtil.retrieveSettingsForActiveAccount();
|
||||||
logger.log("Retrieved settings:", settings);
|
|
||||||
|
|
||||||
const notifyingNewActivity = !!settings?.notifyingNewActivityTime;
|
const notifyingNewActivity = !!settings?.notifyingNewActivityTime;
|
||||||
const notifyingReminder = !!settings?.notifyingReminderTime;
|
const notifyingReminder = !!settings?.notifyingReminderTime;
|
||||||
|
|
||||||
if (!notifyingNewActivity || !notifyingReminder) {
|
if (!notifyingNewActivity || !notifyingReminder) {
|
||||||
allGoingOff = true;
|
allGoingOff = true;
|
||||||
logger.log("Both notifications are being turned off.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log("Checking service worker readiness...");
|
|
||||||
await navigator.serviceWorker?.ready
|
await navigator.serviceWorker?.ready
|
||||||
.then((registration) => {
|
.then((registration) => {
|
||||||
logger.log("Service worker is ready. Fetching subscription...");
|
|
||||||
return registration.pushManager.getSubscription();
|
return registration.pushManager.getSubscription();
|
||||||
})
|
})
|
||||||
.then(async (subscript: PushSubscription | null) => {
|
.then(async (subscript: PushSubscription | null) => {
|
||||||
if (subscript) {
|
if (subscript) {
|
||||||
subscription = subscript.toJSON();
|
subscription = subscript.toJSON();
|
||||||
logger.log("PushSubscription retrieved:", subscription);
|
|
||||||
|
|
||||||
if (allGoingOff) {
|
if (allGoingOff) {
|
||||||
logger.log("Unsubscribing from push notifications...");
|
|
||||||
await subscript.unsubscribe();
|
await subscript.unsubscribe();
|
||||||
logger.log("Successfully unsubscribed.");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logConsoleAndDb("Subscription object is not available.");
|
logConsoleAndDb("Subscription object is not available.");
|
||||||
logger.log("No subscription found.");
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -441,7 +431,6 @@ export default class App extends Vue {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
logger.log("No subscription available. Notifying user...");
|
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -451,21 +440,14 @@ export default class App extends Vue {
|
|||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
);
|
);
|
||||||
logger.log("Exiting as there is no subscription to process.");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverSubscription = {
|
const serverSubscription = {
|
||||||
...subscription,
|
...(subscription as PushSubscriptionJSON),
|
||||||
|
...(allGoingOff ? {} : { notifyType: notification.title }),
|
||||||
};
|
};
|
||||||
if (!allGoingOff) {
|
|
||||||
serverSubscription["notifyType"] = notification.title;
|
|
||||||
logger.log(
|
|
||||||
`Server subscription updated with notifyType: ${notification.title}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log("Sending unsubscribe request to the server...");
|
|
||||||
const pushServerSuccess = await fetch("/web-push/unsubscribe", {
|
const pushServerSuccess = await fetch("/web-push/unsubscribe", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -482,7 +464,6 @@ export default class App extends Vue {
|
|||||||
);
|
);
|
||||||
logger.error("Push server error response:", errorBody);
|
logger.error("Push server error response:", errorBody);
|
||||||
}
|
}
|
||||||
logger.log(`Server response status: ${response.status}`);
|
|
||||||
return response.ok;
|
return response.ok;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -497,7 +478,6 @@ export default class App extends Vue {
|
|||||||
const message = pushServerSuccess
|
const message = pushServerSuccess
|
||||||
? "Notification is off."
|
? "Notification is off."
|
||||||
: "Notification is still on. Try to turn it off again.";
|
: "Notification is still on. Try to turn it off again.";
|
||||||
logger.log("Server response processed. Message:", message);
|
|
||||||
|
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
@@ -510,14 +490,9 @@ export default class App extends Vue {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (notification.callback) {
|
if (notification.callback) {
|
||||||
logger.log("Executing notification callback...");
|
|
||||||
notification.callback(pushServerSuccess);
|
notification.callback(pushServerSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(
|
|
||||||
"Completed turnOffNotifications with success:",
|
|
||||||
pushServerSuccess,
|
|
||||||
);
|
|
||||||
return pushServerSuccess;
|
return pushServerSuccess;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logConsoleAndDb(
|
logConsoleAndDb(
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ async function validateMigrationApplication<T>(
|
|||||||
await sqlQuery(
|
await sqlQuery(
|
||||||
`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`,
|
`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`,
|
||||||
);
|
);
|
||||||
logger.log(`✅ [Migration-Validation] Table ${tableName} exists`);
|
// Reduced logging - only log on error
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
validation.isValid = false;
|
validation.isValid = false;
|
||||||
validation.errors.push(`Table ${tableName} missing`);
|
validation.errors.push(`Table ${tableName} missing`);
|
||||||
@@ -269,9 +269,7 @@ async function validateMigrationApplication<T>(
|
|||||||
try {
|
try {
|
||||||
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`);
|
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`);
|
||||||
validation.hasExpectedColumns = true;
|
validation.hasExpectedColumns = true;
|
||||||
logger.log(
|
// Reduced logging - only log on error
|
||||||
`✅ [Migration-Validation] Column iViewContent exists in contacts table`,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
validation.isValid = false;
|
validation.isValid = false;
|
||||||
validation.errors.push(
|
validation.errors.push(
|
||||||
@@ -333,18 +331,16 @@ async function isSchemaAlreadyPresent<T>(
|
|||||||
const hasTable =
|
const hasTable =
|
||||||
result?.values?.length > 0 ||
|
result?.values?.length > 0 ||
|
||||||
(Array.isArray(result) && result.length > 0);
|
(Array.isArray(result) && result.length > 0);
|
||||||
logger.log(
|
// Reduced logging - only log on error
|
||||||
`🔍 [Migration-Schema] Initial migration schema check - accounts table exists: ${hasTable}`,
|
|
||||||
);
|
|
||||||
return hasTable;
|
return hasTable;
|
||||||
} else if (migration.name === "002_add_iViewContent_to_contacts") {
|
} else if (migration.name === "002_add_iViewContent_to_contacts") {
|
||||||
// Check if iViewContent column exists in contacts table
|
// Check if iViewContent column exists in contacts table
|
||||||
try {
|
try {
|
||||||
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`);
|
await sqlQuery(`SELECT iViewContent FROM contacts LIMIT 1`);
|
||||||
logger.log(`🔍 [Migration-Schema] iViewContent column already exists`);
|
// Reduced logging - only log on error
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log(`🔍 [Migration-Schema] iViewContent column does not exist`);
|
// Reduced logging - only log on error
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +350,7 @@ async function isSchemaAlreadyPresent<T>(
|
|||||||
// // Check if future migration schema already exists
|
// // Check if future migration schema already exists
|
||||||
// }
|
// }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log(
|
logger.error(
|
||||||
`🔍 [Migration-Schema] Schema check failed for ${migration.name}, assuming not present:`,
|
`🔍 [Migration-Schema] Schema check failed for ${migration.name}, assuming not present:`,
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
@@ -413,83 +409,55 @@ export async function runMigrations<T>(
|
|||||||
|
|
||||||
// Step 1: Create migrations table if it doesn't exist
|
// Step 1: Create migrations table if it doesn't exist
|
||||||
// Note: We use IF NOT EXISTS here because this is infrastructure, not a business migration
|
// Note: We use IF NOT EXISTS here because this is infrastructure, not a business migration
|
||||||
logger.log(
|
|
||||||
"🔧 [Migration] Creating migrations table if it doesn't exist...",
|
|
||||||
);
|
|
||||||
await sqlExec(`
|
await sqlExec(`
|
||||||
CREATE TABLE IF NOT EXISTS migrations (
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
name TEXT PRIMARY KEY,
|
name TEXT PRIMARY KEY,
|
||||||
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
|
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
`);
|
`);
|
||||||
logger.log("✅ [Migration] Migrations table ready");
|
|
||||||
|
|
||||||
// Step 2: Get list of already applied migrations
|
// Step 2: Get list of already applied migrations
|
||||||
logger.log("🔍 [Migration] Querying existing migrations...");
|
|
||||||
const appliedMigrationsResult = await sqlQuery(
|
const appliedMigrationsResult = await sqlQuery(
|
||||||
"SELECT name FROM migrations",
|
"SELECT name FROM migrations",
|
||||||
);
|
);
|
||||||
logger.log("📊 [Migration] Raw query result:", appliedMigrationsResult);
|
|
||||||
|
|
||||||
const appliedMigrations = extractMigrationNames(appliedMigrationsResult);
|
const appliedMigrations = extractMigrationNames(appliedMigrationsResult);
|
||||||
logger.log(
|
|
||||||
"📋 [Migration] Extracted applied migrations:",
|
|
||||||
Array.from(appliedMigrations),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Step 3: Get all registered migrations
|
// Step 3: Get all registered migrations
|
||||||
const migrations = migrationRegistry.getMigrations();
|
const migrations = migrationRegistry.getMigrations();
|
||||||
|
|
||||||
if (migrations.length === 0) {
|
if (migrations.length === 0) {
|
||||||
logger.warn("⚠️ [Migration] No migrations registered");
|
logger.warn("⚠️ [Migration] No migrations registered");
|
||||||
logger.warn("[MigrationService] No migrations registered");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
`📊 [Migration] Found ${migrations.length} total migrations, ${appliedMigrations.size} already applied`,
|
`📊 [Migration] Found ${migrations.length} total migrations, ${appliedMigrations.size} already applied`,
|
||||||
);
|
);
|
||||||
logger.log(
|
|
||||||
`📝 [Migration] Registered migrations: ${migrations.map((m) => m.name).join(", ")}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
let appliedCount = 0;
|
let appliedCount = 0;
|
||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
|
|
||||||
// Step 4: Process each migration
|
// Step 4: Process each migration
|
||||||
for (const migration of migrations) {
|
for (const migration of migrations) {
|
||||||
logger.log(`\n🔍 [Migration] Processing migration: ${migration.name}`);
|
|
||||||
|
|
||||||
// Check 1: Is it recorded as applied in migrations table?
|
// Check 1: Is it recorded as applied in migrations table?
|
||||||
const isRecordedAsApplied = appliedMigrations.has(migration.name);
|
const isRecordedAsApplied = appliedMigrations.has(migration.name);
|
||||||
|
|
||||||
// Check 2: Does the schema already exist in the database?
|
// Check 2: Does the schema already exist in the database?
|
||||||
const isSchemaPresent = await isSchemaAlreadyPresent(migration, sqlQuery);
|
const isSchemaPresent = await isSchemaAlreadyPresent(migration, sqlQuery);
|
||||||
|
|
||||||
logger.log(
|
|
||||||
`🔍 [Migration] ${migration.name} - Recorded: ${isRecordedAsApplied}, Schema: ${isSchemaPresent}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Skip if already recorded as applied
|
// Skip if already recorded as applied
|
||||||
if (isRecordedAsApplied) {
|
if (isRecordedAsApplied) {
|
||||||
logger.log(
|
|
||||||
`⏭️ [Migration] Skipping already applied: ${migration.name}`,
|
|
||||||
);
|
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle case where schema exists but isn't recorded
|
// Handle case where schema exists but isn't recorded
|
||||||
if (isSchemaPresent) {
|
if (isSchemaPresent) {
|
||||||
logger.log(
|
|
||||||
`🔄 [Migration] Schema exists but not recorded. Marking ${migration.name} as applied...`,
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
const insertResult = await sqlExec(
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [
|
||||||
"INSERT INTO migrations (name) VALUES (?)",
|
migration.name,
|
||||||
[migration.name],
|
]);
|
||||||
);
|
|
||||||
logger.log(`✅ [Migration] Migration record inserted:`, insertResult);
|
|
||||||
logger.log(
|
logger.log(
|
||||||
`✅ [Migration] Marked existing schema as applied: ${migration.name}`,
|
`✅ [Migration] Marked existing schema as applied: ${migration.name}`,
|
||||||
);
|
);
|
||||||
@@ -509,11 +477,7 @@ export async function runMigrations<T>(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Execute the migration SQL
|
// Execute the migration SQL
|
||||||
logger.log(`🔧 [Migration] Executing SQL for ${migration.name}...`);
|
|
||||||
await sqlExec(migration.sql);
|
await sqlExec(migration.sql);
|
||||||
logger.log(
|
|
||||||
`✅ [Migration] SQL executed successfully for ${migration.name}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate the migration was applied correctly
|
// Validate the migration was applied correctly
|
||||||
const validation = await validateMigrationApplication(
|
const validation = await validateMigrationApplication(
|
||||||
@@ -525,26 +489,14 @@ export async function runMigrations<T>(
|
|||||||
`⚠️ [Migration] Validation failed for ${migration.name}:`,
|
`⚠️ [Migration] Validation failed for ${migration.name}:`,
|
||||||
validation.errors,
|
validation.errors,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
logger.log(
|
|
||||||
`✅ [Migration] Schema validation passed for ${migration.name}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record that the migration was applied
|
// Record that the migration was applied
|
||||||
logger.log(
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [
|
||||||
`📝 [Migration] Recording migration ${migration.name} as applied...`,
|
migration.name,
|
||||||
);
|
]);
|
||||||
const insertResult = await sqlExec(
|
|
||||||
"INSERT INTO migrations (name) VALUES (?)",
|
|
||||||
[migration.name],
|
|
||||||
);
|
|
||||||
logger.log(`✅ [Migration] Migration record inserted:`, insertResult);
|
|
||||||
|
|
||||||
logger.log(`🎉 [Migration] Successfully applied: ${migration.name}`);
|
logger.log(`🎉 [Migration] Successfully applied: ${migration.name}`);
|
||||||
logger.info(
|
|
||||||
`[MigrationService] Successfully applied migration: ${migration.name}`,
|
|
||||||
);
|
|
||||||
appliedCount++;
|
appliedCount++;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`❌ [Migration] Error applying ${migration.name}:`, error);
|
logger.error(`❌ [Migration] Error applying ${migration.name}:`, error);
|
||||||
@@ -569,11 +521,7 @@ export async function runMigrations<T>(
|
|||||||
migration,
|
migration,
|
||||||
sqlQuery,
|
sqlQuery,
|
||||||
);
|
);
|
||||||
if (validation.isValid) {
|
if (!validation.isValid) {
|
||||||
logger.log(
|
|
||||||
`✅ [Migration] Schema validation passed for ${migration.name}`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`⚠️ [Migration] Schema validation failed for ${migration.name}:`,
|
`⚠️ [Migration] Schema validation failed for ${migration.name}:`,
|
||||||
validation.errors,
|
validation.errors,
|
||||||
@@ -582,21 +530,10 @@ export async function runMigrations<T>(
|
|||||||
|
|
||||||
// Mark the migration as applied since the schema change already exists
|
// Mark the migration as applied since the schema change already exists
|
||||||
try {
|
try {
|
||||||
logger.log(
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [
|
||||||
`📝 [Migration] Attempting to record ${migration.name} as applied despite error...`,
|
migration.name,
|
||||||
);
|
]);
|
||||||
const insertResult = await sqlExec(
|
|
||||||
"INSERT INTO migrations (name) VALUES (?)",
|
|
||||||
[migration.name],
|
|
||||||
);
|
|
||||||
logger.log(
|
|
||||||
`✅ [Migration] Migration record inserted after error:`,
|
|
||||||
insertResult,
|
|
||||||
);
|
|
||||||
logger.log(`✅ [Migration] Marked as applied: ${migration.name}`);
|
logger.log(`✅ [Migration] Marked as applied: ${migration.name}`);
|
||||||
logger.info(
|
|
||||||
`[MigrationService] Successfully marked migration as applied: ${migration.name}`,
|
|
||||||
);
|
|
||||||
appliedCount++;
|
appliedCount++;
|
||||||
} catch (insertError) {
|
} catch (insertError) {
|
||||||
// If we can't insert the migration record, log it but don't fail
|
// If we can't insert the migration record, log it but don't fail
|
||||||
@@ -604,10 +541,6 @@ export async function runMigrations<T>(
|
|||||||
`⚠️ [Migration] Could not record ${migration.name} as applied:`,
|
`⚠️ [Migration] Could not record ${migration.name} as applied:`,
|
||||||
insertError,
|
insertError,
|
||||||
);
|
);
|
||||||
logger.warn(
|
|
||||||
`[MigrationService] Could not record migration ${migration.name} as applied:`,
|
|
||||||
insertError,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For other types of errors, still fail the migration
|
// For other types of errors, still fail the migration
|
||||||
@@ -615,25 +548,14 @@ export async function runMigrations<T>(
|
|||||||
`❌ [Migration] Failed to apply ${migration.name}:`,
|
`❌ [Migration] Failed to apply ${migration.name}:`,
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
logger.error(
|
|
||||||
`[MigrationService] Failed to apply migration ${migration.name}:`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
throw new Error(`Migration ${migration.name} failed: ${error}`);
|
throw new Error(`Migration ${migration.name} failed: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Final validation - verify all migrations are properly recorded
|
// Step 5: Final validation - verify all migrations are properly recorded
|
||||||
logger.log(
|
|
||||||
"\n🔍 [Migration] Final validation - checking migrations table...",
|
|
||||||
);
|
|
||||||
const finalMigrationsResult = await sqlQuery("SELECT name FROM migrations");
|
const finalMigrationsResult = await sqlQuery("SELECT name FROM migrations");
|
||||||
const finalAppliedMigrations = extractMigrationNames(finalMigrationsResult);
|
const finalAppliedMigrations = extractMigrationNames(finalMigrationsResult);
|
||||||
logger.log(
|
|
||||||
"📋 [Migration] Final applied migrations:",
|
|
||||||
Array.from(finalAppliedMigrations),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check that all expected migrations are recorded
|
// Check that all expected migrations are recorded
|
||||||
const expectedMigrations = new Set(migrations.map((m) => m.name));
|
const expectedMigrations = new Set(migrations.map((m) => m.name));
|
||||||
@@ -645,17 +567,10 @@ export async function runMigrations<T>(
|
|||||||
logger.warn(
|
logger.warn(
|
||||||
`⚠️ [Migration] Missing migration records: ${missingMigrations.join(", ")}`,
|
`⚠️ [Migration] Missing migration records: ${missingMigrations.join(", ")}`,
|
||||||
);
|
);
|
||||||
logger.warn(
|
|
||||||
`[MigrationService] Missing migration records: ${missingMigrations.join(", ")}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(`\n🎉 [Migration] Migration process complete!`);
|
|
||||||
logger.log(
|
logger.log(
|
||||||
`📊 [Migration] Summary: Applied: ${appliedCount}, Skipped: ${skippedCount}, Total: ${migrations.length}`,
|
`🎉 [Migration] Migration process complete! Summary: ${appliedCount} applied, ${skippedCount} skipped`,
|
||||||
);
|
|
||||||
logger.info(
|
|
||||||
`[MigrationService] Migration process complete. Applied: ${appliedCount}, Skipped: ${skippedCount}`,
|
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("\n💥 [Migration] Migration process failed:", error);
|
logger.error("\n💥 [Migration] Migration process failed:", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user