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
13 changes: 13 additions & 0 deletions config/cron-tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,17 @@ module.exports = {
"0 0 * * 1": async ({ strapi }) => {
await strapi.service("api::recommendation.recommendation").applyTimeDecay(0.9);
},

/**
* Internal Reconciliation: matches wallet balance with transaction ledger.
* Runs every hour at minute 0.
*/
"0 * * * *": async ({ strapi }) => {
strapi.log.info('[Cron] Triggering Wallet Reconciliation Audit...');
try {
await strapi.service('api::wallet.wallet').runInternalReconciliation();
} catch (err) {
strapi.log.error(`[Cron] Reconciliation failed: ${err.message}`);
}
},
};
2 changes: 1 addition & 1 deletion config/middlewares.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = ({ env }) => {
// Build the allowed origins list dynamically
const allowedOrigins = [
// "http://localhost:3000",
"http://127.0.0.1:5173",
// "http://localhost:1338",
// "http://localhost:5173",
// "https://axe-code.vercel.app",
Expand Down
18 changes: 2 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
"bcrypt": "^6.0.0",
"better-sqlite3": "^12.2.0",
"dockerode": "^4.0.8",
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.3",
"nodemailer": "^7.0.3",
"pg": "^8.20.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.30.3",
Expand All @@ -36,7 +37,6 @@
"tar-stream": "^3.1.7",
"validator": "^13.15.26",
"web-push": "^3.6.7",
"pg": "^8.20.0",
"zod": "^4.3.6"
},
"engines": {
Expand Down
36 changes: 36 additions & 0 deletions scratch/check-db-pg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

const strapi = require('@strapi/strapi');

async function check() {
const app = await strapi().load();
const knex = app.db.connection;

try {
const tables = await knex.raw("SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public'");
const tableNames = tables.rows.map(t => t.tablename);
console.log('Tables:', tableNames.join(', '));

const walletRelated = tableNames.filter(t => t.includes('wallet'));
console.log('Wallet related:', walletRelated);

const txRelated = tableNames.filter(t => t.includes('transaction'));
console.log('Transaction related:', txRelated);

const payoutRelated = tableNames.filter(t => t.includes('payout'));
console.log('Payout related:', payoutRelated);

if (tableNames.includes('transactions')) {
const count = await knex('transactions').count();
console.log('Transaction count:', count);
const sample = await knex('transactions').select('*').limit(5);
console.log('Sample transactions:', sample);
}

} catch (err) {
console.error('Error:', err.message);
} finally {
process.exit(0);
}
}

check();
21 changes: 21 additions & 0 deletions scratch/check-db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

const Database = require('better-sqlite3');
const db = new Database('.tmp/data.db');

try {
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
console.log('Tables:', tables.map(t => t.name).join(', '));

const walletTables = tables.filter(t => t.name.toLowerCase().includes('wallet'));
console.log('Wallet related tables:', walletTables.map(t => t.name).join(', '));

const transactionTables = tables.filter(t => t.name.toLowerCase().includes('transaction'));
console.log('Transaction related tables:', transactionTables.map(t => t.name).join(', '));

const payoutTables = tables.filter(t => t.name.toLowerCase().includes('payout'));
console.log('Payout related tables:', payoutTables.map(t => t.name).join(', '));


} catch (err) {
console.error('Error:', err.message);
}
23 changes: 23 additions & 0 deletions scratch/check-ownership.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

async function checkDB() {
try {
const entitlements = await strapi.documents('api::entitlement.entitlement').findMany({
status: 'published'
});
console.log('--- Entitlements ---');
console.log(JSON.stringify(entitlements, null, 2));

const userEntitlements = await strapi.documents('api::user-entitlement.user-entitlement').findMany({
populate: ['users_permissions_user']
});
console.log('--- User Entitlements ---');
console.log(JSON.stringify(userEntitlements, null, 2));

process.exit(0);
} catch (err) {
console.error(err);
process.exit(1);
}
}

checkDB();
20 changes: 20 additions & 0 deletions scratch/check_trans.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

const knex = require('knex')({
client: 'pg',
connection: 'postgres://postgres:0194456244@127.0.0.1:5432/postgres'
});

async function run() {
try {
const rows = await knex('transactions').orderBy('id', 'desc').limit(10);
console.log('LATEST_TRANSACTIONS_START');
console.log(JSON.stringify(rows, null, 2));
console.log('LATEST_TRANSACTIONS_END');
} catch (err) {
console.error('ERROR:', err.message);
} finally {
await knex.destroy();
process.exit(0);
}
}
run();
48 changes: 48 additions & 0 deletions scratch/diagnose-db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

const fs = require('fs');

async function diagnose() {
const report = { timestamp: new Date().toISOString(), logs: [] };

try {
// 1. Check all Entitlements
const entitlements = await strapi.documents('api::entitlement.entitlement').findMany();
report.entitlements = entitlements.map(e => ({ id: e.id, docId: e.documentId, itemId: e.itemId, type: e.content_types }));

// 2. Check all User Entitlements (Ownerships)
const ownerships = await strapi.documents('api::user-entitlement.user-entitlement').findMany({
populate: ['users_permissions_user']
});
report.ownerships = ownerships.map(o => ({
id: o.id,
productId: o.productId,
type: o.content_types,
user: o.users_permissions_user?.username,
userId: o.users_permissions_user?.id,
userDocId: o.users_permissions_user?.documentId
}));

// 3. Check all Payments
const payments = await strapi.documents('api::payment.payment').findMany({
populate: ['user', 'course', 'event']
});
report.payments = payments.map(p => ({
id: p.id,
paymobId: p.paymob_id,
status: p.status,
user: p.user?.username,
courseId: p.course?.documentId,
eventId: p.event?.documentId
}));

fs.writeFileSync('scratch/db-report.json', JSON.stringify(report, null, 2));
console.log('Diagnostic report generated in scratch/db-report.json');
process.exit(0);
} catch (err) {
console.error(err);
fs.writeFileSync('scratch/db-report-error.txt', err.message);
process.exit(1);
}
}

diagnose();
28 changes: 28 additions & 0 deletions scratch/fix-ownership.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

async function fixOwnership() {
try {
const userDocId = 'uz8iqakhfl73cwvid7lgs505'; // memo
const entitlementDocId = 'fzwixyo7gr2avqq9p36yla94'; // The course he bought
const contentType = 'course';

console.log(`Fixing ownership for user ${userDocId} and entitlement ${entitlementDocId}`);

await strapi.documents('api::user-entitlement.user-entitlement').create({
data: {
productId: entitlementDocId,
content_types: contentType,
users_permissions_user: userDocId,
publishedAt: new Date()
},
status: 'published'
});

console.log('Ownership fixed successfully!');
process.exit(0);
} catch (err) {
console.error('Fix failed:', err);
process.exit(1);
}
}

fixOwnership();
35 changes: 35 additions & 0 deletions scratch/fix_links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

const strapi = require('@strapi/strapi');

async function fixLinks() {
const app = await strapi().load();
try {
console.log('Fixing transaction links...');

// Find all transactions without a wallet
const transactions = await app.db.query('api::transaction.transaction').findMany({
populate: ['wallet']
});

const unlinked = transactions.filter(t => !t.wallet);
console.log(`Found ${unlinked.length} unlinked transactions.`);

for (const t of unlinked) {
// Based on our investigation, Wallet ID 2 is the main publisher wallet
// We will link CREDIT transactions that look like course purchases to it
if (t.type === 'CREDIT' && t.description.includes('course purchase')) {
await app.documents('api::transaction.transaction').update({
documentId: t.documentId,
data: { wallet: 2 }
});
console.log(`Linked Transaction #${t.id} to Wallet #2`);
}
}
} catch (err) {
console.error('Fix failed:', err);
} finally {
process.exit(0);
}
}

fixLinks();
Empty file added scratch/full-diag.json
Empty file.
Loading
Loading