From 78f76f35b99093bbfa6684f9d77695028ab6f6c2 Mon Sep 17 00:00:00 2001 From: javayhu Date: Sun, 10 Aug 2025 11:48:53 +0800 Subject: [PATCH] feat: add basic auth to distribute credits cron jobs --- env.example | 13 ++++++++ src/app/api/distribute-credits/route.ts | 42 ++++++++++++++++++++++++- src/credits/credits.ts | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/env.example b/env.example index 74477e8..33f05ab 100644 --- a/env.example +++ b/env.example @@ -167,6 +167,12 @@ TURNSTILE_SECRET_KEY="" # ----------------------------------------------------------------------------- NEXT_PUBLIC_CRISP_WEBSITE_ID="" +# ----------------------------------------------------------------------------- +# Cron Jobs +# ----------------------------------------------------------------------------- +CRON_JOBS_USERNAME="" +CRON_JOBS_PASSWORD="" + # ----------------------------------------------------------------------------- # AI # https://mksaas.com/docs/ai @@ -179,6 +185,13 @@ GOOGLE_GENERATIVE_AI_API_KEY="" DEEPSEEK_API_KEY="" OPENROUTER_API_KEY="" +# ----------------------------------------------------------------------------- +# Basic Authentication +# Used for protecting sensitive API endpoints like distribute-credits +# ----------------------------------------------------------------------------- +BASIC_AUTH_USERNAME="admin" +BASIC_AUTH_PASSWORD="" + # ----------------------------------------------------------------------------- # Web Content Analyzer (Firecrawl) # https://firecrawl.dev/ diff --git a/src/app/api/distribute-credits/route.ts b/src/app/api/distribute-credits/route.ts index d8e55f4..244a316 100644 --- a/src/app/api/distribute-credits/route.ts +++ b/src/app/api/distribute-credits/route.ts @@ -1,10 +1,50 @@ import { distributeCreditsToAllUsers } from '@/credits/credits'; import { NextResponse } from 'next/server'; +// Basic authentication middleware +function validateBasicAuth(request: Request): boolean { + const authHeader = request.headers.get('authorization'); + + if (!authHeader || !authHeader.startsWith('Basic ')) { + return false; + } + + // Extract credentials from Authorization header + const base64Credentials = authHeader.split(' ')[1]; + const credentials = Buffer.from(base64Credentials, 'base64').toString( + 'utf-8' + ); + const [username, password] = credentials.split(':'); + + // Validate against environment variables + const expectedUsername = process.env.CRON_JOBS_USERNAME; + const expectedPassword = process.env.CRON_JOBS_PASSWORD; + + if (!expectedUsername || !expectedPassword) { + console.error( + 'Basic auth credentials not configured in environment variables' + ); + return false; + } + + return username === expectedUsername && password === expectedPassword; +} + /** * distribute credits to all users daily */ -export async function GET() { +export async function GET(request: Request) { + // Validate basic authentication + if (!validateBasicAuth(request)) { + console.error('distribute credits unauthorized'); + return new NextResponse('Unauthorized', { + status: 401, + headers: { + 'WWW-Authenticate': 'Basic realm="Secure Area"', + }, + }); + } + console.log('distribute credits start'); const { processedCount, errorCount } = await distributeCreditsToAllUsers(); console.log( diff --git a/src/credits/credits.ts b/src/credits/credits.ts index d2f3055..a6c5ce7 100644 --- a/src/credits/credits.ts +++ b/src/credits/credits.ts @@ -571,7 +571,7 @@ export async function distributeCreditsToAllUsers() { name: user.name, }) .from(user) - .where(eq(user.banned, false)); // Only active users + .where(not(eq(user.banned, true))); // Only active users, banned is null by default console.log('distribute credits, users count:', users.length); let processedCount = 0;