feat: add admin authentication and management features
- Implemented Prisma schema with models for AdminUser, AppSettings, and Snapshot. - Created seed script to initialize the database with an admin user and JWT secret. - Developed admin login page with form handling and error management. - Added API routes for admin login, logout, change password, and JWT verification. - Integrated Stripe for payment intent management in admin orders. - Established middleware for protecting admin routes with JWT authentication. - Created Zustand stores for managing persona and snapshot states.
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
generator client {
|
||||
provider = "prisma-client"
|
||||
output = "../src/generated/prisma"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
}
|
||||
|
||||
model AdminUser {
|
||||
id String @id @default(cuid())
|
||||
username String @unique
|
||||
passwordHash String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model AppSettings {
|
||||
key String @id
|
||||
value String
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Snapshot {
|
||||
paymentIntentId String @id
|
||||
imageData String
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { PrismaClient } from '../src/generated/prisma/client.js';
|
||||
import { PrismaLibSql } from '@prisma/adapter-libsql';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { randomBytes } from 'crypto';
|
||||
import path from 'path';
|
||||
|
||||
const dbPath = path.resolve(process.cwd(), 'prisma', 'lootah.db');
|
||||
const adapter = new PrismaLibSql({ url: `file:${dbPath}` });
|
||||
const prisma = new PrismaClient({ adapter } as ConstructorParameters<typeof PrismaClient>[0]);
|
||||
|
||||
async function main() {
|
||||
console.log('Seeding database...');
|
||||
|
||||
// Create default admin user
|
||||
const existingAdmin = await prisma.adminUser.findUnique({
|
||||
where: { username: 'admin' },
|
||||
});
|
||||
|
||||
if (!existingAdmin) {
|
||||
const passwordHash = await bcrypt.hash('admin123', 12);
|
||||
await prisma.adminUser.create({
|
||||
data: {
|
||||
username: 'admin',
|
||||
passwordHash,
|
||||
},
|
||||
});
|
||||
console.log('✓ Created admin user (username: admin, password: admin123)');
|
||||
console.log(' ⚠️ Change the password after first login!');
|
||||
} else {
|
||||
console.log('✓ Admin user already exists, skipping.');
|
||||
}
|
||||
|
||||
// Generate and store JWT secret
|
||||
const existingSecret = await prisma.appSettings.findUnique({
|
||||
where: { key: 'jwt_secret' },
|
||||
});
|
||||
|
||||
if (!existingSecret) {
|
||||
const jwtSecret = randomBytes(64).toString('hex');
|
||||
await prisma.appSettings.upsert({
|
||||
where: { key: 'jwt_secret' },
|
||||
update: { value: jwtSecret },
|
||||
create: { key: 'jwt_secret', value: jwtSecret },
|
||||
});
|
||||
console.log('✓ Generated JWT secret and stored in database.');
|
||||
} else {
|
||||
console.log('✓ JWT secret already exists, skipping.');
|
||||
}
|
||||
|
||||
console.log('Seeding complete!');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user