2024-03-04 01:05:23 -05:00
|
|
|
import { generateStandardId } from '@logto/shared/universal';
|
2024-03-16 06:04:55 -05:00
|
|
|
import { sql } from '@silverhand/slonik';
|
2024-03-04 01:05:23 -05:00
|
|
|
|
|
|
|
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
|
|
|
|
|
|
type ActiveUserInteractionLog = {
|
|
|
|
tenantId: string;
|
|
|
|
userId: string;
|
|
|
|
createdAt: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
const alteration: AlterationScript = {
|
|
|
|
up: async (pool) => {
|
|
|
|
// Delete all record from `daily_active_users` table
|
|
|
|
await pool.query(sql`delete from daily_active_users;`);
|
|
|
|
|
|
|
|
// Retrieve all active user logs from `logs` table
|
|
|
|
const { rows: interactionLogs } = await pool.query<ActiveUserInteractionLog>(sql`
|
|
|
|
select tenant_id, payload->>'userId' as user_id, created_at
|
|
|
|
from logs
|
|
|
|
where payload->>'userId' is not null and key like 'ExchangeTokenBy.%' and payload->>'result' = 'Success'
|
|
|
|
`);
|
|
|
|
|
|
|
|
if (interactionLogs.length === 0) {
|
|
|
|
console.log('No active user interaction logs found, skip alteration');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate DAU data from active user logs
|
|
|
|
for (const { tenantId, userId, createdAt } of interactionLogs) {
|
|
|
|
/**
|
|
|
|
* Note: we ignore the conflict here because conflict data may be inserted when staging.
|
|
|
|
*/
|
|
|
|
// eslint-disable-next-line no-await-in-loop
|
|
|
|
await pool.query(sql`
|
|
|
|
insert into daily_active_users (id, tenant_id, user_id, date)
|
|
|
|
values (${generateStandardId()},${tenantId}, ${userId}, ${new Date(
|
|
|
|
createdAt
|
|
|
|
).toISOString()})
|
|
|
|
on conflict do nothing;
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
down: async (pool) => {
|
|
|
|
// Cannot be reverted
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export default alteration;
|