Initial commit: full-stack Bun + Elysia + React template
Elysia.js API with session-based auth (email/password + Google OAuth), role system (USER/ADMIN/SUPER_ADMIN), Prisma + PostgreSQL, React 19 with Mantine UI, TanStack Router, dark theme, and comprehensive test suite (unit, integration, E2E with Lightpanda). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
34
prisma/migrations/20260331061237_init/migration.sql
Normal file
34
prisma/migrations/20260331061237_init/migration.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "user" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "session" (
|
||||
"id" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "session_token_idx" ON "session"("token");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
5
prisma/migrations/20260331084655_add_role/migration.sql
Normal file
5
prisma/migrations/20260331084655_add_role/migration.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN', 'SUPER_ADMIN');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "user" ADD COLUMN "role" "Role" NOT NULL DEFAULT 'USER';
|
||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
42
prisma/schema.prisma
Normal file
42
prisma/schema.prisma
Normal file
@@ -0,0 +1,42 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../generated/prisma"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
enum Role {
|
||||
USER
|
||||
ADMIN
|
||||
SUPER_ADMIN
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
email String @unique
|
||||
password String
|
||||
role Role @default(USER)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
sessions Session[]
|
||||
|
||||
@@map("user")
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(uuid())
|
||||
token String @unique
|
||||
userId String
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([token])
|
||||
@@map("session")
|
||||
}
|
||||
39
prisma/seed.ts
Normal file
39
prisma/seed.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { PrismaClient } from '../generated/prisma'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
const SUPER_ADMIN_EMAILS = (process.env.SUPER_ADMIN_EMAIL ?? '').split(',').map(e => e.trim()).filter(Boolean)
|
||||
|
||||
async function main() {
|
||||
const users = [
|
||||
{ name: 'Super Admin', email: 'superadmin@example.com', password: 'superadmin123', role: 'SUPER_ADMIN' as const },
|
||||
{ name: 'Admin', email: 'admin@example.com', password: 'admin123', role: 'ADMIN' as const },
|
||||
{ name: 'User', email: 'user@example.com', password: 'user123', role: 'USER' as const },
|
||||
]
|
||||
|
||||
for (const u of users) {
|
||||
const hashed = await Bun.password.hash(u.password, { algorithm: 'bcrypt' })
|
||||
await prisma.user.upsert({
|
||||
where: { email: u.email },
|
||||
update: { name: u.name, password: hashed, role: u.role },
|
||||
create: { name: u.name, email: u.email, password: hashed, role: u.role },
|
||||
})
|
||||
console.log(`Seeded: ${u.email} (${u.role})`)
|
||||
}
|
||||
|
||||
// Promote super admin emails from env
|
||||
for (const email of SUPER_ADMIN_EMAILS) {
|
||||
const user = await prisma.user.findUnique({ where: { email } })
|
||||
if (user && user.role !== 'SUPER_ADMIN') {
|
||||
await prisma.user.update({ where: { email }, data: { role: 'SUPER_ADMIN' } })
|
||||
console.log(`Promoted to SUPER_ADMIN: ${email}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(() => prisma.$disconnect())
|
||||
Reference in New Issue
Block a user