- Mock purchase: order create → mock-pay → FlashToken issued instantly (no real billing) - Flash page (/flash/:token): esp-web-tools integration, token state display, consume on complete - Orders route: create/mock-pay/me/refund with full audit logging - Flash route: GET validate, GET manifest (esp-web-tools compatible), POST consume - MinIO file proxy (/api/files/*): browser CORS solved, firmware served through backend - Schema: chipFamily on Project, flashOffset on ProjectFile - ProjectNew: chipFamily selector + firmware flash offset option - MyOrders: real order list with flash token status and buttons - Dockerfile: prisma db push (no migration files needed for dev) - TOSS_PAYMENT_GUIDE.md: step-by-step guide for real payment after business registration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
205 lines
5.0 KiB
Plaintext
205 lines
5.0 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum UserRole {
|
|
admin
|
|
seller
|
|
buyer
|
|
}
|
|
|
|
enum ProjectStatus {
|
|
draft
|
|
pending
|
|
approved
|
|
rejected
|
|
suspended
|
|
}
|
|
|
|
enum OrderStatus {
|
|
pending
|
|
paid
|
|
cancelled
|
|
refunded
|
|
}
|
|
|
|
enum PaymentGateway {
|
|
toss
|
|
stripe
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
email String @unique
|
|
passwordHash String
|
|
nickname String @unique
|
|
role UserRole @default(buyer)
|
|
profileImageUrl String?
|
|
isEmailVerified Boolean @default(false)
|
|
isActive Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
lastLoginAt DateTime?
|
|
lastLoginIp String?
|
|
|
|
projects Project[]
|
|
orders Order[]
|
|
reviews Review[]
|
|
auditLogs AuditLog[]
|
|
}
|
|
|
|
model Project {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id])
|
|
title String
|
|
description String @db.Text
|
|
difficultyLevel Int @default(3)
|
|
chipFamily String @default("ESP32-S3")
|
|
requiredParts Json?
|
|
status ProjectStatus @default(draft)
|
|
adminNote String?
|
|
commissionRate Float @default(0.1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
files ProjectFile[]
|
|
product Product?
|
|
}
|
|
|
|
model ProjectFile {
|
|
id String @id @default(uuid())
|
|
projectId String
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
fileType String
|
|
url String
|
|
thumbnailUrl String?
|
|
fileSize Int
|
|
mimeType String
|
|
originalName String?
|
|
flashOffset String @default("0x0")
|
|
displayOrder Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model Product {
|
|
id String @id @default(uuid())
|
|
projectId String @unique
|
|
project Project @relation(fields: [projectId], references: [id])
|
|
price Int
|
|
isOnSale Boolean @default(true)
|
|
totalSales Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
orders Order[]
|
|
reviews Review[]
|
|
}
|
|
|
|
model Order {
|
|
id String @id @default(uuid())
|
|
buyerId String
|
|
buyer User @relation(fields: [buyerId], references: [id])
|
|
productId String
|
|
product Product @relation(fields: [productId], references: [id])
|
|
amount Int
|
|
commissionAmount Int
|
|
sellerAmount Int
|
|
paymentGateway PaymentGateway @default(toss)
|
|
paymentKey String?
|
|
tossOrderId String? @unique
|
|
status OrderStatus @default(pending)
|
|
buyerInfo Json
|
|
deviceInfo Json?
|
|
orderedAt DateTime @default(now())
|
|
paidAt DateTime?
|
|
refundedAt DateTime?
|
|
refundReason String?
|
|
|
|
flashToken FlashToken?
|
|
review Review?
|
|
}
|
|
|
|
model FlashToken {
|
|
id String @id @default(uuid())
|
|
token String @unique @default(uuid())
|
|
orderId String @unique
|
|
order Order @relation(fields: [orderId], references: [id])
|
|
isUsed Boolean @default(false)
|
|
usedAt DateTime?
|
|
macAddress String?
|
|
chipFamily String?
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
flashLog FlashLog?
|
|
}
|
|
|
|
model FlashLog {
|
|
id String @id @default(uuid())
|
|
flashTokenId String @unique
|
|
flashToken FlashToken @relation(fields: [flashTokenId], references: [id])
|
|
macAddress String
|
|
chipFamily String
|
|
firmwareName String
|
|
firmwareId String
|
|
success Boolean
|
|
errorMessage String?
|
|
clientIp String
|
|
userAgent String?
|
|
flashedAt DateTime @default(now())
|
|
}
|
|
|
|
model Review {
|
|
id String @id @default(uuid())
|
|
orderId String @unique
|
|
order Order @relation(fields: [orderId], references: [id])
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id])
|
|
productId String
|
|
product Product @relation(fields: [productId], references: [id])
|
|
rating Int
|
|
title String
|
|
content String @db.Text
|
|
isVisible Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
media ReviewMedia[]
|
|
}
|
|
|
|
model ReviewMedia {
|
|
id String @id @default(uuid())
|
|
reviewId String
|
|
review Review @relation(fields: [reviewId], references: [id], onDelete: Cascade)
|
|
mediaType String
|
|
url String
|
|
thumbnailUrl String?
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(uuid())
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id])
|
|
action String
|
|
targetType String?
|
|
targetId String?
|
|
ipAddress String
|
|
userAgent String?
|
|
requestMethod String?
|
|
requestPath String?
|
|
requestBody Json?
|
|
responseStatus Int?
|
|
metadata Json?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([action])
|
|
@@index([createdAt])
|
|
}
|