diff --git a/create-db-worker/src/delete-stale-workflow.ts b/create-db-worker/src/delete-stale-workflow.ts new file mode 100644 index 0000000..58d2dd5 --- /dev/null +++ b/create-db-worker/src/delete-stale-workflow.ts @@ -0,0 +1,75 @@ +import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers'; + +type Params = Record; + +type Env = { + INTEGRATION_TOKEN: string; +}; + +type Project = { + id: string; + name: string; + createdAt: string; +}; + +type ProjectsResponse = { + data: Project[]; + pagination: { + nextCursor: string | null; + hasMore: boolean; + }; +}; + +export class DeleteStaleProjectsWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep): Promise { + const res = await step.do('fetch-projects', async () => { + const response = await fetch('https://api.prisma.io/v1/projects?limit=1000', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.env.INTEGRATION_TOKEN}`, + }, + }); + + if (!response.ok) { + throw new Error(`Failed to fetch projects: ${response.statusText}`); + } + + const data = await response.text(); + return data; + }); + + const projects: ProjectsResponse = JSON.parse(res); + const now = Date.now(); + const twentyFourHours = 24 * 60 * 60 * 1000; + + const staleProjects = projects.data.filter((project) => { + const createdAt = new Date(project.createdAt).getTime(); + return now - createdAt > twentyFourHours; + }); + + console.log(`Total projects: ${projects.data.length}, Stale projects: ${staleProjects.length}`); + + for (const project of staleProjects) { + await step.do(`delete-project-${project.id}`, async () => { + const deleteRes = await fetch(`https://api.prisma.io/v1/projects/${project.id}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.env.INTEGRATION_TOKEN}`, + }, + }); + + if (!deleteRes.ok) { + throw new Error(`Failed to delete project ${project.id}: ${deleteRes.statusText}`); + } + + console.log(`Deleted stale project: ${project.id} (${project.name})`); + }); + } + + console.log(`Finished deleting ${staleProjects.length} stale projects`); + } +} + +export default DeleteStaleProjectsWorkflow; diff --git a/create-db-worker/src/index.ts b/create-db-worker/src/index.ts index 9f14415..f202f7c 100644 --- a/create-db-worker/src/index.ts +++ b/create-db-worker/src/index.ts @@ -1,15 +1,17 @@ import DeleteDbWorkflow from './delete-workflow'; +import DeleteStaleProjectsWorkflow from './delete-stale-workflow'; import { PosthogEventCapture } from './analytics'; interface Env { INTEGRATION_TOKEN: string; DELETE_DB_WORKFLOW: Workflow; + DELETE_STALE_WORKFLOW: Workflow; CREATE_DB_RATE_LIMITER: RateLimit; CREATE_DB_DATASET: AnalyticsEngineDataset; POSTHOG_API_KEY?: string; POSTHOG_API_HOST?: string; } -export { DeleteDbWorkflow }; +export { DeleteDbWorkflow, DeleteStaleProjectsWorkflow }; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { @@ -193,4 +195,9 @@ export default { // --- Fallback: Method not allowed --- return new Response('Method Not Allowed', { status: 405 }); }, + + async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise { + console.log('Scheduled event triggered:', controller.cron); + ctx.waitUntil(env.DELETE_STALE_WORKFLOW.create({ params: {} })); + }, } satisfies ExportedHandler; diff --git a/create-db-worker/wrangler.jsonc b/create-db-worker/wrangler.jsonc index 5081aab..07913a8 100644 --- a/create-db-worker/wrangler.jsonc +++ b/create-db-worker/wrangler.jsonc @@ -14,7 +14,15 @@ "binding": "DELETE_DB_WORKFLOW", "class_name": "DeleteDbWorkflow", }, + { + "name": "delete-stale-workflow", + "binding": "DELETE_STALE_WORKFLOW", + "class_name": "DeleteStaleProjectsWorkflow", + }, ], + "triggers": { + "crons": ["0 0 * * *"], + }, "analytics_engine_datasets": [ { "binding": "CREATE_DB_DATASET",