@sonatel-os/strapi-plugin-tcg 0.3.1 → 0.3.2

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.
@@ -235,14 +235,28 @@ const ACTIONS = [
235
235
  { section: "plugins", uid: RBAC.PLAYERS_READ, displayName: "Read players" },
236
236
  { section: "plugins", uid: RBAC.PLAYERS_WRITE, displayName: "Deactivate players, grant cards" },
237
237
  { section: "plugins", uid: RBAC.REDEMPTIONS_READ, displayName: "Read redemptions" },
238
- { section: "plugins", uid: RBAC.REDEMPTIONS_FULFILL, displayName: "Fulfill or cancel redemptions (booth)" },
238
+ {
239
+ section: "plugins",
240
+ uid: RBAC.REDEMPTIONS_FULFILL,
241
+ displayName: "Fulfill or cancel redemptions (booth)"
242
+ },
239
243
  { section: "plugins", uid: RBAC.AUDIT_READ, displayName: "Read the pull audit log" },
240
244
  { section: "plugins", uid: RBAC.REWARDS_READ, displayName: "Read the reward catalog" },
241
245
  { section: "plugins", uid: RBAC.REWARDS_WRITE, displayName: "Edit the reward catalog" },
242
246
  { section: "plugins", uid: RBAC.LIVE_READ, displayName: "Read live boosts" },
243
247
  { section: "plugins", uid: RBAC.LIVE_WRITE, displayName: "Create and edit live boosts" },
244
- { section: "settings", category: CATEGORY, uid: RBAC.SETTINGS_READ, displayName: "Read settings" },
245
- { section: "settings", category: CATEGORY, uid: RBAC.SETTINGS_WRITE, displayName: "Edit settings" }
248
+ {
249
+ section: "settings",
250
+ category: CATEGORY,
251
+ uid: RBAC.SETTINGS_READ,
252
+ displayName: "Read settings"
253
+ },
254
+ {
255
+ section: "settings",
256
+ category: CATEGORY,
257
+ uid: RBAC.SETTINGS_WRITE,
258
+ displayName: "Edit settings"
259
+ }
246
260
  ].map((a) => ({ ...a, pluginName: PLUGIN_ID }));
247
261
  const timers = [];
248
262
  const UNIQUE_COLUMNS = [
@@ -250,21 +264,46 @@ const UNIQUE_COLUMNS = [
250
264
  ["plugin::tcg.player", "code"],
251
265
  ["plugin::tcg.redemption", "code"]
252
266
  ];
267
+ const hasIndex = async (knex, table, name) => {
268
+ const client = String(knex.client?.config?.client ?? "");
269
+ if (/mysql/.test(client)) {
270
+ const rows2 = await knex.raw("SHOW INDEX FROM ?? WHERE Key_name = ?", [table, name]);
271
+ return (rows2?.[0] ?? []).length > 0;
272
+ }
273
+ if (/pg|postgres/.test(client)) {
274
+ const rows2 = await knex.raw("SELECT 1 FROM pg_indexes WHERE tablename = ? AND indexname = ?", [
275
+ table,
276
+ name
277
+ ]);
278
+ return (rows2?.rows ?? []).length > 0;
279
+ }
280
+ const rows = await knex.raw("PRAGMA index_list(??)", [table]);
281
+ return (rows ?? []).some((r) => r.name === name);
282
+ };
253
283
  const ensureUniqueIndexes = async (strapi) => {
284
+ const knex = strapi.db.connection;
254
285
  for (const [uid, column] of UNIQUE_COLUMNS) {
255
286
  const table = strapi.db.metadata.get(uid)?.tableName;
256
287
  if (!table) continue;
257
288
  const name = `${table}_${column}_tcg_uq`;
258
289
  try {
259
- await strapi.db.connection.schema.alterTable(table, (t) => t.unique([column], { indexName: name }));
290
+ if (await hasIndex(knex, table, name)) continue;
291
+ await knex.schema.alterTable(table, (t) => t.unique([column], { indexName: name }));
292
+ strapi.log.info(`${LOG_PREFIX} unique index ${name} created`);
260
293
  } catch (err) {
261
- if (!/exist|duplicate/i.test(err.message)) strapi.log.warn(`${LOG_PREFIX} unique index ${name}: ${err.message}`);
294
+ if (!/exist|duplicate/i.test(err.message))
295
+ strapi.log.warn(`${LOG_PREFIX} unique index ${name}: ${err.message}`);
262
296
  }
263
297
  }
264
298
  };
265
299
  const bootstrap = async ({ strapi }) => {
266
300
  await strapi.service("admin::permission").actionProvider.registerMany(ACTIONS);
267
- await ensureUniqueIndexes(strapi);
301
+ setTimeout(
302
+ () => ensureUniqueIndexes(strapi).catch(
303
+ (err) => strapi.log.warn(`${LOG_PREFIX} unique indexes: ${err.message}`)
304
+ ),
305
+ 5e3
306
+ );
268
307
  const plugin = strapi.plugin(PLUGIN_ID);
269
308
  if (plugin.service("settings").sweepsEnabled()) {
270
309
  const safe = (label, fn) => async () => {
@@ -275,13 +314,21 @@ const bootstrap = async ({ strapi }) => {
275
314
  strapi.log.warn(`${LOG_PREFIX} sweep ${label} failed: ${err.message}`);
276
315
  }
277
316
  };
278
- const nonceTimer = setInterval(safe("nonces", () => plugin.service("qr").sweep()), NONCE_SWEEP_INTERVAL_MS);
279
- const tradeTimer = setInterval(safe("expired offers", () => plugin.service("trade").sweep()), TRADE_SWEEP_INTERVAL_MS);
317
+ const nonceTimer = setInterval(
318
+ safe("nonces", () => plugin.service("qr").sweep()),
319
+ NONCE_SWEEP_INTERVAL_MS
320
+ );
321
+ const tradeTimer = setInterval(
322
+ safe("expired offers", () => plugin.service("trade").sweep()),
323
+ TRADE_SWEEP_INTERVAL_MS
324
+ );
280
325
  nonceTimer.unref();
281
326
  tradeTimer.unref();
282
327
  timers.push(nonceTimer, tradeTimer);
283
328
  }
284
- strapi.log.info(`${LOG_PREFIX} ready — ${ACTIONS.length} RBAC actions, content API at /api/${PLUGIN_ID}`);
329
+ strapi.log.info(
330
+ `${LOG_PREFIX} ready — ${ACTIONS.length} RBAC actions, content API at /api/${PLUGIN_ID}`
331
+ );
285
332
  };
286
333
  const stopTimers = () => {
287
334
  while (timers.length) clearInterval(timers.pop());
@@ -234,14 +234,28 @@ const ACTIONS = [
234
234
  { section: "plugins", uid: RBAC.PLAYERS_READ, displayName: "Read players" },
235
235
  { section: "plugins", uid: RBAC.PLAYERS_WRITE, displayName: "Deactivate players, grant cards" },
236
236
  { section: "plugins", uid: RBAC.REDEMPTIONS_READ, displayName: "Read redemptions" },
237
- { section: "plugins", uid: RBAC.REDEMPTIONS_FULFILL, displayName: "Fulfill or cancel redemptions (booth)" },
237
+ {
238
+ section: "plugins",
239
+ uid: RBAC.REDEMPTIONS_FULFILL,
240
+ displayName: "Fulfill or cancel redemptions (booth)"
241
+ },
238
242
  { section: "plugins", uid: RBAC.AUDIT_READ, displayName: "Read the pull audit log" },
239
243
  { section: "plugins", uid: RBAC.REWARDS_READ, displayName: "Read the reward catalog" },
240
244
  { section: "plugins", uid: RBAC.REWARDS_WRITE, displayName: "Edit the reward catalog" },
241
245
  { section: "plugins", uid: RBAC.LIVE_READ, displayName: "Read live boosts" },
242
246
  { section: "plugins", uid: RBAC.LIVE_WRITE, displayName: "Create and edit live boosts" },
243
- { section: "settings", category: CATEGORY, uid: RBAC.SETTINGS_READ, displayName: "Read settings" },
244
- { section: "settings", category: CATEGORY, uid: RBAC.SETTINGS_WRITE, displayName: "Edit settings" }
247
+ {
248
+ section: "settings",
249
+ category: CATEGORY,
250
+ uid: RBAC.SETTINGS_READ,
251
+ displayName: "Read settings"
252
+ },
253
+ {
254
+ section: "settings",
255
+ category: CATEGORY,
256
+ uid: RBAC.SETTINGS_WRITE,
257
+ displayName: "Edit settings"
258
+ }
245
259
  ].map((a) => ({ ...a, pluginName: PLUGIN_ID }));
246
260
  const timers = [];
247
261
  const UNIQUE_COLUMNS = [
@@ -249,21 +263,46 @@ const UNIQUE_COLUMNS = [
249
263
  ["plugin::tcg.player", "code"],
250
264
  ["plugin::tcg.redemption", "code"]
251
265
  ];
266
+ const hasIndex = async (knex, table, name) => {
267
+ const client = String(knex.client?.config?.client ?? "");
268
+ if (/mysql/.test(client)) {
269
+ const rows2 = await knex.raw("SHOW INDEX FROM ?? WHERE Key_name = ?", [table, name]);
270
+ return (rows2?.[0] ?? []).length > 0;
271
+ }
272
+ if (/pg|postgres/.test(client)) {
273
+ const rows2 = await knex.raw("SELECT 1 FROM pg_indexes WHERE tablename = ? AND indexname = ?", [
274
+ table,
275
+ name
276
+ ]);
277
+ return (rows2?.rows ?? []).length > 0;
278
+ }
279
+ const rows = await knex.raw("PRAGMA index_list(??)", [table]);
280
+ return (rows ?? []).some((r) => r.name === name);
281
+ };
252
282
  const ensureUniqueIndexes = async (strapi) => {
283
+ const knex = strapi.db.connection;
253
284
  for (const [uid, column] of UNIQUE_COLUMNS) {
254
285
  const table = strapi.db.metadata.get(uid)?.tableName;
255
286
  if (!table) continue;
256
287
  const name = `${table}_${column}_tcg_uq`;
257
288
  try {
258
- await strapi.db.connection.schema.alterTable(table, (t) => t.unique([column], { indexName: name }));
289
+ if (await hasIndex(knex, table, name)) continue;
290
+ await knex.schema.alterTable(table, (t) => t.unique([column], { indexName: name }));
291
+ strapi.log.info(`${LOG_PREFIX} unique index ${name} created`);
259
292
  } catch (err) {
260
- if (!/exist|duplicate/i.test(err.message)) strapi.log.warn(`${LOG_PREFIX} unique index ${name}: ${err.message}`);
293
+ if (!/exist|duplicate/i.test(err.message))
294
+ strapi.log.warn(`${LOG_PREFIX} unique index ${name}: ${err.message}`);
261
295
  }
262
296
  }
263
297
  };
264
298
  const bootstrap = async ({ strapi }) => {
265
299
  await strapi.service("admin::permission").actionProvider.registerMany(ACTIONS);
266
- await ensureUniqueIndexes(strapi);
300
+ setTimeout(
301
+ () => ensureUniqueIndexes(strapi).catch(
302
+ (err) => strapi.log.warn(`${LOG_PREFIX} unique indexes: ${err.message}`)
303
+ ),
304
+ 5e3
305
+ );
267
306
  const plugin = strapi.plugin(PLUGIN_ID);
268
307
  if (plugin.service("settings").sweepsEnabled()) {
269
308
  const safe = (label, fn) => async () => {
@@ -274,13 +313,21 @@ const bootstrap = async ({ strapi }) => {
274
313
  strapi.log.warn(`${LOG_PREFIX} sweep ${label} failed: ${err.message}`);
275
314
  }
276
315
  };
277
- const nonceTimer = setInterval(safe("nonces", () => plugin.service("qr").sweep()), NONCE_SWEEP_INTERVAL_MS);
278
- const tradeTimer = setInterval(safe("expired offers", () => plugin.service("trade").sweep()), TRADE_SWEEP_INTERVAL_MS);
316
+ const nonceTimer = setInterval(
317
+ safe("nonces", () => plugin.service("qr").sweep()),
318
+ NONCE_SWEEP_INTERVAL_MS
319
+ );
320
+ const tradeTimer = setInterval(
321
+ safe("expired offers", () => plugin.service("trade").sweep()),
322
+ TRADE_SWEEP_INTERVAL_MS
323
+ );
279
324
  nonceTimer.unref();
280
325
  tradeTimer.unref();
281
326
  timers.push(nonceTimer, tradeTimer);
282
327
  }
283
- strapi.log.info(`${LOG_PREFIX} ready — ${ACTIONS.length} RBAC actions, content API at /api/${PLUGIN_ID}`);
328
+ strapi.log.info(
329
+ `${LOG_PREFIX} ready — ${ACTIONS.length} RBAC actions, content API at /api/${PLUGIN_ID}`
330
+ );
284
331
  };
285
332
  const stopTimers = () => {
286
333
  while (timers.length) clearInterval(timers.pop());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonatel-os/strapi-plugin-tcg",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "A full trading-card-game module for Strapi v5: seasons, packs, cards, pulls, trades, redemptions — with admin screens and RBAC.",
5
5
  "license": "MIT",
6
6
  "author": "LPIX-11 <mohamed.johnson@orange-sonatel.com>",