Files
Philip e7b7536e70 Add password change, rename Viewer→Commissioner, fix login logo
- Rename VIEWER role to COMMISSIONER throughout (schema, middleware,
  admin layout, users page); add psql pre-migration step in entrypoint
  to rename the PostgreSQL enum value without data loss
- Install postgresql-client in Docker runner stage for psql access
- Login page: fetch sbLogo from settings API instead of hardcoded path
- Password change for all authenticated users:
  - New PATCH /api/users/me endpoint (verifies current password, hashes new)
  - Change Password button/modal on /my-squares page
  - Change Password link in admin sidebar (links to /my-squares)
  - New password_change email template (seeded, editable in admin)
  - sendPasswordChangedEmail auto-email triggered on change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 10:36:16 -07:00

124 lines
5.0 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
// Seed 100 squares (positions "00" through "99")
const squares = [];
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const position = `${row}${col}`;
squares.push({ position });
}
}
for (const square of squares) {
await prisma.square.upsert({
where: { position: square.position },
update: {},
create: square,
});
}
// Seed default game settings
await prisma.gameSettings.upsert({
where: { id: 'singleton' },
update: {},
create: {
id: 'singleton',
title: 'Super Bowl Squares',
commissioner: 'Commissioner',
eventName: 'Super Bowl',
betAmount: 10,
winFirstPct: 20,
winSecondPct: 20,
winThirdPct: 20,
winFinalPct: 30,
donationPct: 10,
graceHours: 48,
},
});
// Seed default score row
await prisma.score.upsert({
where: { id: 'singleton' },
update: {},
create: { id: 'singleton' },
});
// Seed default email settings
await prisma.emailSettings.upsert({
where: { id: 'singleton' },
update: {},
create: { id: 'singleton' },
});
// Seed default email templates
const templates = [
{
name: 'welcome',
subject: 'Welcome to {{eventName}}!',
body: 'Hi {{name}},\n\nWelcome to {{eventName}} Squares!\n\nYour account has been created successfully.\n\nUsername: {{email}}\n\nYou can log in and view the game board at:\n{{gameUrl}}/login\n\nThanks for joining!\n{{commissioner}}',
},
{
name: 'square_confirmation',
subject: 'Square Purchase Confirmation - {{eventName}}',
body: 'Hi {{name}},\n\nThank you for purchasing squares for {{eventName}}!\n\nSquare(s): {{squares}}\nAmount Due: ${{amountDue}}\n\nPlease submit payment within {{graceHours}} hours to keep your squares.\n\nPayment Instructions:\n{{paymentInstructions}}\n\nPayment Methods:\n{{paymentMethods}}\n\nView your squares at: {{gameUrl}}\n\nThanks,\n{{commissioner}}',
},
{
name: 'square_confirmed',
subject: 'Payment Confirmed - {{eventName}}',
body: 'Hi {{name}},\n\nGreat news! Your payment has been confirmed for {{eventName}}.\n\nSquare(s): {{squares}}\n\nYour squares are now locked in. View the game board at:\n{{gameUrl}}\n\nGood luck!\n{{commissioner}}',
},
{
name: 'square_released',
subject: 'Square Released - {{eventName}}',
body: 'Hi {{name}},\n\nYour square(s) for {{eventName}} have been released.\n\nSquare(s): {{squares}}\n\nIf you believe this is an error, please contact the commissioner.\n\nView the game board at: {{gameUrl}}\n\n{{commissioner}}',
},
{
name: 'payment_reminder',
subject: 'Payment Reminder - {{eventName}}',
body: 'Hi {{name}},\n\nThis is a friendly reminder that your payment for {{eventName}} is due soon!\n\nSquare(s): {{squares}}\nAmount Due: ${{amountDue}}\n\nYour grace period expires in approximately 2 hours. After that, your squares may be released.\n\nPayment Instructions:\n{{paymentInstructions}}\n\nPayment Methods:\n{{paymentMethods}}\n\nView your squares at: {{gameUrl}}\n\nThanks,\n{{commissioner}}',
},
{
name: 'winner_notification',
subject: 'Congratulations! You won {{quarter}} - {{eventName}}',
body: 'Hi {{name}},\n\nCongratulations! You are the winner of the {{quarter}} quarter!\n\nSquare: {{square}}\nScore: {{nfcTeam}} {{nfcScore}} - {{afcTeam}} {{afcScore}}\nPrize: ${{prize}}\n\nThanks,\n{{commissioner}}',
},
{
name: 'numbers_assigned',
subject: 'Numbers Have Been Assigned - {{eventName}}',
body: 'Hi {{name}},\n\nThe random numbers have been assigned for {{eventName}}!\n\nVisit the game board to see your numbers and check your squares:\n{{gameUrl}}\n\nGood luck!\n{{commissioner}}',
},
{
name: 'game_results',
subject: 'Final Results - {{eventName}}',
body: 'Hi {{name}},\n\nThe game is over! Here are the final results for {{eventName}}:\n\nWinners:\n{{winners}}\n\nCongratulations to all the winners!\n\nThank you for participating in this year\'s Super Bowl Squares. We hope you had a great time!\n\nView the final board at: {{gameUrl}}\n\n{{commissioner}}',
},
{
name: 'password_change',
subject: 'Password Changed - {{eventName}}',
body: 'Hi {{name}},\n\nYour password for {{eventName}} Squares has been changed successfully.\n\nIf you did not make this change, please contact the commissioner immediately.\n\nYou can log in at: {{gameUrl}}/login\n\n{{commissioner}}',
},
];
for (const template of templates) {
await prisma.emailTemplate.upsert({
where: { name: template.name },
update: {},
create: template,
});
}
console.log('Seed completed successfully');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});