Skip to content

Commit

Permalink
Merge pull request #741 from hackclub/signup-lottery-rebased
Browse files Browse the repository at this point in the history
Signup lottery rebased
  • Loading branch information
maxwofford authored Nov 13, 2024
2 parents b3e7599 + 2d57e34 commit 8a5b24b
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { sql } from '@vercel/postgres'

export default async function createBackgroundJob(
type: string,
type: 'run_lottery' | 'create_person' | 'invite',
args: {},
status: 'pending' | 'completed' | 'failed' = 'pending',
) {
Expand Down
15 changes: 15 additions & 0 deletions src/app/api/cron/every-day/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const dynamic = 'force-dynamic'
export const fetchCache = 'force-no-store'

import createBackgroundJob from '../create-background-job'

async function processDailyJobs() {
console.log('Processing daily jobs')

await createBackgroundJob('run_lottery', {})
}

export async function GET() {
await processDailyJobs()
return Response.json({ success: true })
}
17 changes: 4 additions & 13 deletions src/app/api/cron/every-minute/route.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import type { NextRequest } from 'next/server'
import { processBackgroundJobs } from './process-background-jobs'
import { processBackgroundJobs } from '../process-background-jobs'

export async function GET(request: NextRequest) {
const authHeader = request.headers.get('authorization')
const isDev = process.env.NODE_ENV === 'development'
if (!isDev && authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response('Unauthorized', {
status: 401,
})
}
export const dynamic = 'force-dynamic'
export const fetchCache = 'force-no-store'

export async function GET() {
await processBackgroundJobs()

return Response.json({ success: true })
}

export const maxDuration = 60
export const fetchCache = 'force-no-store'
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use server'

import { sql } from '@vercel/postgres'
import Airtable from 'airtable'

async function processPendingInviteJobs() {
const { rows } =
Expand Down Expand Up @@ -120,9 +121,110 @@ async function processPendingPersonInitJobs() {
)
}

async function processLotteryJobs() {
const { rows } = await sql`
SELECT *
FROM background_job
WHERE type = 'run_lottery'
AND status = 'pending'
LIMIT 1;
`

if (rows.length === 0) {
return
}

const previous = (
await sql`
SELECT *
FROM background_job
WHERE type = 'run_lottery'
AND status = 'completed'
ORDER BY created_at DESC
LIMIT 1;`
).rows[0]

console.log('Previous lottery job', previous)

if (
previous &&
previous.created_at > new Date(Date.now() - 1000 * 60 * 60 * 23)
) {
return
}

Airtable.configure({
apiKey: process.env.AIRTABLE_API_KEY,
endpointUrl: process.env.AIRTABLE_ENDPOINT_URL,
})

const base = Airtable.base('appTeNFYcUiYfGcR6')

const highSeasChannelId = 'C07PZMBUNDS'
await base('arrpheus_message_requests').create({
message_text: `Each day, a newly signed up user will win a free Raspberry Pi Zero! Today's winner is...`,
target_slack_id: highSeasChannelId,
requester_identifier: 'cron-job',
})

// read all free sticker orders created in the last 24 hours
const eligibleUsers = await base('people')
.select({
filterByFormula: `AND(
has_ordered_free_stickers = TRUE(),
verified_eligible = TRUE(),
verified_ineligible = FALSE(),
DATETIME_DIFF(NOW(), verification_updated_at, 'hours') <= 24
)`,
})
.all()

const winner = eligibleUsers.sort(() => Math.random() - 0.5)[0]
console.log('Winner', winner)

// create the order
const order = await base('shop_orders').create({
status: 'fresh',
shop_item: ['recKV56D2PATOqK4W'],
recipient: [winner?.id],
})

// send a DM to the winner
const messageRequests = [
{
message_text: `Hey, congrats <@${winner?.fields['slack_id']}>! You won today's free Raspberry Pi Zero! 🎉 We're shipping it to the same address as your sticker bundle.`,
target_slack_id: winner?.fields['slack_id'],
requester_identifier: 'cron-job',
},
{
message_text: `Heads up, <@${winner?.fields['slack_id']}> won today's Raspberry Pi Zero! 🎉`,
target_slack_id: 'U0C7B14Q3', // notify msw for observability
requester_identifier: 'cron-job',
},
{
message_text: `Congratulations to <@${winner?.fields['slack_id']}> for winning a free Raspberry Pi Zero! 🎉 Every day a newly signed up person will get one.`,
target_slack_id: highSeasChannelId,
requester_identifier: 'cron-job',
},
]

const messagePromise = base('arrpheus_message_requests').create(
messageRequests.map((m) => ({ fields: m })),
)

const upsert = await sql`
UPDATE background_job
SET status='completed',
output=${JSON.stringify(order)}
WHERE type='run_lottery'
AND status='pending'`

await Promise.all([upsert, messagePromise])
}
export async function processBackgroundJobs() {
await Promise.all([
processPendingInviteJobs(),
processPendingPersonInitJobs(),
processLotteryJobs(),
])
}
2 changes: 1 addition & 1 deletion src/app/marketing/marketing-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Airtable from 'airtable'
import { createWaka } from '../utils/waka'
import { getSession } from '../utils/auth'

import createBackgroundJob from '../api/cron/every-minute/create-background-job'
import createBackgroundJob from '../api/cron/create-background-job'

const highSeasPeopleTable = () => {
const highSeasBaseId = process.env.BASE_ID
Expand Down
5 changes: 3 additions & 2 deletions src/app/signout/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export const dynamic = 'force-dynamic'

import { redirect } from 'next/navigation'
import { NextRequest } from 'next/server'
import { deleteSession } from '../utils/auth'

export async function GET(request: NextRequest) {
export async function GET() {
console.log('SIGNING OUT!!!!!!')
await deleteSession()
return redirect('/')
Expand Down
24 changes: 22 additions & 2 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
person,
} from './app/utils/data'

export async function middleware(request: NextRequest) {
export async function userPageMiddleware(request: NextRequest) {
const session = await getSession()
const slackId = session?.slackId

Expand Down Expand Up @@ -125,6 +125,26 @@ export async function middleware(request: NextRequest) {
return response
}

const cronjobMiddleware = async (request: NextRequest) => {
const authHeader = request.headers.get('authorization')
const isDev = process.env.NODE_ENV === 'development'
if (!isDev && authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 },
)
}
return NextResponse.next()
}

export async function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/api/cron')) {
return cronjobMiddleware(request)
} else {
return userPageMiddleware(request)
}
}

export const config = {
matcher: ['/signpost', '/shipyard', '/wonderdome', '/shop'],
matcher: ['/signpost', '/shipyard', '/wonderdome', '/shop', '/api/cron/'],
}
4 changes: 4 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
{
"path": "/api/cron/every-minute",
"schedule": "* * * * *"
},
{
"path": "/api/cron/every-day",
"schedule": "0 18 * * *"
}
]
}

0 comments on commit 8a5b24b

Please sign in to comment.