Merge pull request #79 from MkSaaSHQ/dev/cron-jobs

support cron jobs
This commit is contained in:
javayhu 2025-08-10 11:16:48 +08:00 committed by GitHub
commit f50f60443a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 34 additions and 1800 deletions

View File

@ -167,12 +167,6 @@ TURNSTILE_SECRET_KEY=""
# -----------------------------------------------------------------------------
NEXT_PUBLIC_CRISP_WEBSITE_ID=""
# -----------------------------------------------------------------------------
# Inngest
# https://mksaas.com/docs/jobs#setup
# -----------------------------------------------------------------------------
INNGEST_SIGNING_KEY=""
# -----------------------------------------------------------------------------
# AI
# https://mksaas.com/docs/ai

View File

@ -100,7 +100,6 @@
"fumadocs-core": "^15.6.7",
"fumadocs-mdx": "^11.7.3",
"fumadocs-ui": "^15.6.7",
"inngest": "^3.40.1",
"input-otp": "^1.4.2",
"lucide-react": "^0.483.0",
"motion": "^12.4.3",

1708
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
import { distributeCreditsToAllUsers } from '@/credits/credits';
import { NextResponse } from 'next/server';
/**
* distribute credits to all users daily
*/
export async function GET() {
console.log('distribute credits start');
const { processedCount, errorCount } = await distributeCreditsToAllUsers();
console.log(
`distribute credits end, processed: ${processedCount}, errors: ${errorCount}`
);
return NextResponse.json({
message: `distribute credits success, processed: ${processedCount}, errors: ${errorCount}`,
processedCount,
errorCount,
});
}

View File

@ -1,20 +0,0 @@
import { inngest } from '@/inngest/client';
import { NextResponse } from 'next/server';
// Opt out of caching; every request should send a new event
export const dynamic = 'force-dynamic';
// Create a simple async Next.js API route handler
export async function GET() {
console.log('Send event to Inngest start');
// Send your event payload to Inngest
await inngest.send({
name: 'test/hello.world',
data: {
email: 'testUser@example.com',
},
});
console.log('Send event to Inngest end');
return NextResponse.json({ message: 'Event sent!' });
}

View File

@ -1,19 +0,0 @@
import { serve } from 'inngest/next';
import { inngest } from '../../../inngest/client';
import { distributeCreditsDaily, helloWorld } from '../../../inngest/functions';
/**
* Inngest route
*
* https://www.inngest.com/docs/getting-started/nextjs-quick-start
*
* Next.js Edge Functions hosted on Vercel can also stream responses back to Inngest,
* giving you a much higher request timeout of 15 minutes (up from 10 seconds on the Vercel Hobby plan!).
* To enable this, set your runtime to "edge" (see Quickstart for Using Edge Functions | Vercel Docs)
* and add the streaming: "allow" option to your serve handler:
* https://www.inngest.com/docs/learn/serving-inngest-functions#framework-next-js
*/
export const { GET, POST, PUT } = serve({
client: inngest,
functions: [helloWorld, distributeCreditsDaily],
});

View File

@ -559,7 +559,7 @@ export async function addLifetimeMonthlyCredits(userId: string) {
* This function is designed to be called by a cron job
*/
export async function distributeCreditsToAllUsers() {
console.log('distributing credits to all users start');
console.log('distribute credits start');
const db = await getDb();
@ -572,7 +572,7 @@ export async function distributeCreditsToAllUsers() {
})
.from(user)
.where(eq(user.banned, false)); // Only active users
console.log('distributing credits to all users, users count:', users.length);
console.log('distribute credits, users count:', users.length);
let processedCount = 0;
let errorCount = 0;
@ -609,7 +609,7 @@ export async function distributeCreditsToAllUsers() {
processedCount++;
} catch (error) {
console.error(
`distributing credits to all users error, user: ${userRecord.userId}, error:`,
`distribute credits error, user: ${userRecord.userId}, error:`,
error
);
errorCount++;
@ -617,7 +617,7 @@ export async function distributeCreditsToAllUsers() {
}
console.log(
`distributing credits to all users end, processed: ${processedCount}, errors: ${errorCount}`
`distribute credits end, processed: ${processedCount}, errors: ${errorCount}`
);
return { processedCount, errorCount };
}

View File

@ -1,8 +0,0 @@
import { Inngest } from 'inngest';
/**
* Create a client to send and receive events
*
* https://www.inngest.com/docs/getting-started/nextjs-quick-start
*/
export const inngest = new Inngest({ id: 'mksaas-template' });

View File

@ -1,46 +0,0 @@
import { distributeCreditsToAllUsers } from '@/credits/credits';
import { inngest } from './client';
/**
* Distribute credits to all users daily
*
* https://www.inngest.com/docs/guides/scheduled-functions
*/
export const distributeCreditsDaily = inngest.createFunction(
{ id: 'distribute-credits-daily' },
{ cron: 'TZ=Asia/Shanghai 0 1 * * *' },
async ({ step }) => {
// You should use step.run for any async or long-running logic.
// This allows Inngest to track, retry, and visualize each step in your workflow.
await step.run('distribute-credits-to-all-users', async () => {
console.log('distributing credits to all users start');
const { processedCount, errorCount } =
await distributeCreditsToAllUsers();
console.log(
`distributing credits to all users end, processed: ${processedCount}, errors: ${errorCount}`
);
return {
message: `credits distributed, processed: ${processedCount}, errors: ${errorCount}`,
processedCount,
errorCount,
};
});
// you can add new steps here, for example, send email to admin
}
);
/**
* Hello World function, for testing inngest
*
* https://www.inngest.com/docs/guides/scheduled-functions
*/
export const helloWorld = inngest.createFunction(
{ id: 'hello-world' },
{ event: 'test/hello.world' },
async ({ event, step }) => {
console.log('Hello World function start');
await step.sleep('wait-a-moment', '1s');
console.log('Hello World function end');
return { message: `Hello ${event.data.email}!` };
}
);