Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Create the core_sync_states table used by the core-sync service to track
* per-phase watermarks and last-run statistics.
*
* Previously this table was created at runtime by ensureSyncStateTable().
* Moving it to a proper migration ensures the schema exists on boot,
* before any status endpoint queries it.
*/

const TABLE = "core_sync_states"

/* eslint-disable @typescript-eslint/no-explicit-any */
export async function up(knex: any): Promise<void> {
const exists = await knex.schema.hasTable(TABLE)

if (!exists) {
await knex.schema.createTable(TABLE, (t: any) => {
t.string("phase").primary()
t.timestamp("last_synced_at").notNullable()
t.integer("created").defaultTo(0)
t.integer("updated").defaultTo(0)
t.integer("soft_deleted").defaultTo(0)
t.integer("errors").defaultTo(0)
})
return
}

// Table exists from the old runtime creation — add stats columns if missing
const hasCreated = await knex.schema.hasColumn(TABLE, "created")
if (!hasCreated) {
await knex.schema.alterTable(TABLE, (t: any) => {
t.integer("created").defaultTo(0)
t.integer("updated").defaultTo(0)
t.integer("soft_deleted").defaultTo(0)
t.integer("errors").defaultTo(0)
})
}
}

export async function down(knex: any): Promise<void> {
await knex.schema.dropTableIfExists(TABLE)
}
41 changes: 9 additions & 32 deletions apps/cms/src/api/core-sync/services/strapi-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,37 +156,16 @@ export async function buildCoreIdMap(
// ---------------------------------------------------------------------------

const SYNC_STATE_TABLE = "core_sync_states"
let tableEnsured = false

/** Ensure the sync-state table exists with stats columns (idempotent, cached). */
export async function ensureSyncStateTable(strapi: Core.Strapi): Promise<void> {
if (tableEnsured) return
const knex = strapi.db.connection
const exists = await knex.schema.hasTable(SYNC_STATE_TABLE)
if (!exists) {
await knex.schema.createTable(SYNC_STATE_TABLE, (t) => {
t.string("phase").primary()
t.timestamp("last_synced_at").notNullable()
t.integer("created").defaultTo(0)
t.integer("updated").defaultTo(0)
t.integer("soft_deleted").defaultTo(0)
t.integer("errors").defaultTo(0)
})
strapi.log.info(`[core-sync] Created ${SYNC_STATE_TABLE} table`)
} else {
// Migrate existing tables: add stats columns if missing
const hasCreated = await knex.schema.hasColumn(SYNC_STATE_TABLE, "created")
if (!hasCreated) {
await knex.schema.alterTable(SYNC_STATE_TABLE, (t) => {
t.integer("created").defaultTo(0)
t.integer("updated").defaultTo(0)
t.integer("soft_deleted").defaultTo(0)
t.integer("errors").defaultTo(0)
})
strapi.log.info(`[core-sync] Added stats columns to ${SYNC_STATE_TABLE}`)
}
}
tableEnsured = true
/**
* No-op safety net. The table is now created by the database migration
* `2026.04.02T00.00.00.create-core-sync-states.ts` which runs on boot.
* Kept as a function signature so callers don't need to change.
*/
export async function ensureSyncStateTable(
_strapi: Core.Strapi,
): Promise<void> {
// Migration handles table creation and column additions
}

/** Read the last successful sync timestamp for a phase (null = never synced). */
Expand Down Expand Up @@ -216,8 +195,6 @@ export async function getAllSyncTimes(
strapi: Core.Strapi,
): Promise<PersistedPhaseState[]> {
const knex = strapi.db.connection
const exists = await knex.schema.hasTable(SYNC_STATE_TABLE)
if (!exists) return []
const rows = await knex(SYNC_STATE_TABLE)
.select(
"phase",
Expand Down
Loading