added tons of features

This commit is contained in:
2025-02-05 08:01:14 +02:00
parent 4ae0d8c545
commit 8138da6b1d
195 changed files with 12619 additions and 415 deletions

64
lib/db/prisma/client.ts Normal file
View File

@@ -0,0 +1,64 @@
import {PrismaClient} from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
db: PrismaClient
}
export const db =
globalForPrisma.db ||
new PrismaClient({
log: [
{
emit: 'event',
level: 'query'
},
{
emit: 'stdout',
level: 'error'
},
{
emit: 'stdout',
level: 'info'
},
{
emit: 'stdout',
level: 'warn'
}
]
})
export function dbQueryLog() {
db.$on('query' as never, async (e: any) => {
console.log(e.query)
})
}
if (process.env.NODE_ENV !== 'production') globalForPrisma.db = db
// {
// errorFormat: 'pretty',
// log: [
// {
// emit: 'event',
// level: 'query'
// }
// ],
// omit: {
// user: {
// password: true,
// emailVerified: true,
// extendedData: true,
// role: true,
// locale: true,
// status: true,
// updatedAt: true,
// createdAt: true
// },
// store: {}
// }
// }
//
// db.$on('query' as never, async (e: any) => {
// console.debug(e.query)
// })

View File

@@ -0,0 +1,40 @@
model Category {
id Int @id @default(autoincrement())
status Boolean? @default(false)
position Int? @default(0) @db.UnsignedSmallInt
image String? @db.VarChar(384)
storeId Int @map("store_id")
locales CategoryLocale[]
createdAt DateTime @default(now()) @map("created_at")
categoriesOnPruducts CategoriesOnProducts[]
@@map("categories")
}
model CategoryLocale {
id Int @id @default(autoincrement())
category Category @relation(fields: [categoryId], references: [id])
categoryId Int @map("category_id")
lang Lang @default(uk)
title String @db.VarChar(384)
slug String? @unique @db.VarChar(384)
shortTitle String? @map("short_title")
description String? @db.Text
// content String? @db.MediumText
// extendedData Json? @map("extended_data") @db.Json
@@unique([categoryId, lang])
@@map("category_locales")
}
model CategoriesOnProducts {
store Store @relation(fields: [storeId], references: [id])
storeId Int @default(1) @map("store_id")
product Product @relation(fields: [productId], references: [id])
productId Int @map("product_id")
category Category @relation(fields: [categoryId], references: [id])
categoryId Int @map("category_id")
@@id([storeId, productId, categoryId])
@@map("categories_on_pruducts")
}

View File

@@ -0,0 +1,28 @@
enum Lang {
uk
ru
}
enum Unit {
mkg
mg
MO
}
enum ResourceType {
IMAGE
VIDEO
FILE
URI
}
enum Package {
SACHET
TABLET
STICK
DROPS
}
enum ProductType {
DIETARY_SUPPLEMENT
}

View File

@@ -0,0 +1,26 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["typedSql", "prismaSchemaFolder"]
/// relationJoins is not available for MySQL < 8.0.14 and MariaDB.
/// previewFeatures = ["relationJoins"]
}
/// Always after the prisma-client-js generator
generator json {
provider = "prisma-json-types-generator"
// clientOutput = "lib/db/prisma/generated"
}
// generator zod {
// provider = "zod-prisma-types"
// useMultipleFiles = true
// createRelationValuesTypes = true
// }
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}

View File

@@ -0,0 +1,49 @@
enum OpenGraphType {
image
audio
video
article
book
profile
website
}
enum LC {
uk_UA
ru_UA
}
// https://site-ok.com/blog/%D0%BA%D0%B0%D0%BA-%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE-%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D1%8C-%D0%BC%D0%B5%D1%82%D0%B0-%D1%82%D0%B5%D0%B3%D0%B8-title-%D0%B8-description
model Meta {
id Int @id @default(autoincrement())
title String? @db.VarChar(255)
description String? @db.Text
keywords String? @db.VarChar(255)
author String? @db.VarChar(255)
openGraph OpenGraph?
storeLocale StoreLocale[]
productLocale ProductLocale[]
//vendorLocale VendorLocale[]
@@map("meta")
}
// https://ogp.me/#types
// https://seosetups.com/blog/open-graph/
// https://www.conductor.com/academy/open-graph/
// https://developer.x.com/en/docs/x-for-websites/cards/overview/markup
model OpenGraph {
id Int @id @default(autoincrement())
url String? @db.VarChar(1024)
title String? @db.VarChar(384)
description String? @db.Text //
image String? @db.VarChar(1024)
type OpenGraphType?
locale LC?
siteName String? @map("site_name")
video String? @db.VarChar(1024)
meta Meta @relation(fields: [metaId], references: [id], onDelete: Cascade)
metaId Int @unique @map("meta_id")
@@map("open_graph")
}

View File

@@ -0,0 +1,141 @@
model Product {
id Int @id @default(autoincrement())
type ProductType? @default(DIETARY_SUPPLEMENT)
image String? @db.VarChar(512)
// vendor Vendor @relation(fields: [vendorId], references: [id])
// vendorId Int
locales ProductLocale[]
categoriesOnProducts CategoriesOnProducts[]
resources ProductResource[]
toStore ProductToStore[]
attribute ProductAttribute[]
ingradient ProductIngradient[]
form FormsOfRelease[]
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
@@map("products")
}
model ProductLocale {
id Int @id @default(autoincrement())
lang Lang @default(uk)
slug String? @db.VarChar(384)
title String @db.VarChar(384)
shortTitle String? @map("short_title")
headingTitle String? @map("heading_title")
description String? @db.MediumText
content String @db.MediumText
instruction String? @db.MediumText
product Product @relation(fields: [productId], references: [id])
productId Int @map("product_id")
meta Meta? @relation(fields: [metaId], references: [id])
metaId Int? @map("meta_id")
@@unique([productId, slug, lang])
@@map("product_locale")
}
model ProductResource {
id Int @id @default(autoincrement())
type ResourceType
mimeType String? @map("mime_type")
filesize Int? @db.UnsignedInt
width Int? @db.UnsignedSmallInt
height Int? @db.UnsignedSmallInt
quality Int? @db.UnsignedTinyInt
signature String? @db.Char(64)
uri String @db.Text
title String? @db.VarChar(255)
description String? @db.Text
meta Json? @db.Json
productId Int? @map("product_id")
product Product? @relation(fields: [productId], references: [id])
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
@@map("product_resources")
}
model ProductIngradient {
id Int @id @default(autoincrement())
name String
altNames String? @map("alt_names")
genericName String? @map("generic_name")
activeSubstance String? @map("active_substance")
excipients String?
units Unit
amount Decimal @db.Decimal(7, 3)
product Product? @relation(fields: [productId], references: [id])
productId Int? @map("product_id")
@@map("product_ingradients")
}
model ProductAttribute {
id Int @id @default(autoincrement())
lang Lang @default(uk)
key String
text String @db.MediumText
productId Int? @map("product_id")
product Product? @relation(fields: [productId], references: [id])
@@unique([lang, key, productId])
@@map("product_attributes")
}
model ProductToStore {
id Int @id @default(autoincrement())
position Int @default(0)
published Boolean @default(false)
available Boolean @default(false)
price Decimal @default(0) @db.Decimal(7, 2)
pricePromotional Decimal @default(0) @map("price_promotional") @db.Decimal(7, 2)
productId Int @map("product_id")
storeId Int @map("store_id")
product Product @relation(fields: [productId], references: [id])
@@unique([storeId, productId])
@@map("product_to_store")
}
// model Vendor {
// id Int @id @default(autoincrement())
// slug String? @unique @default(uuid()) @db.VarChar(255)
// label String?
// origin String?
// locale VendorLocale[]
// product Product[]
// createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
// updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
//
// @@map("vendors")
// }
//
// model VendorLocale {
// id Int @id @default(autoincrement())
// vendor Vendor? @relation(fields: [vendorId], references: [id])
// vendorId Int? @map("vendor_id")
// lang Lang @default(uk)
// title String @db.VarChar(384)
// slug String? @db.VarChar(384)
// shortTitle String? @map("short_title")
// description String? @db.Text
// content String? @db.MediumText
// meta Meta? @relation(fields: [metaId], references: [id])
// metaId Int? @map("meta_id")
//
// @@unique([vendorId, slug, lang])
// @@map("vendor_locale")
// }
model FormsOfRelease {
id Int @id @default(autoincrement())
quantity Int @db.UnsignedSmallInt
package Package
additionalInfo String? @map("additional_info")
product Product? @relation(fields: [productId], references: [id], onDelete: Cascade)
productId Int? @map("product_id")
@@map("forms_of_release")
}

View File

@@ -0,0 +1,38 @@
enum Currency {
UAH
}
model Store {
id Int @id @default(autoincrement())
uuid String? @unique @default(uuid()) @db.VarChar(255)
active Boolean? @default(false)
slug String? @unique @db.VarChar(255)
image String? @db.VarChar(512)
currency Currency? @default(UAH)
storeLocale StoreLocale[]
categoriesOnPruducts CategoriesOnProducts[]
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
@@map("stores")
}
model StoreLocale {
id Int @id @default(autoincrement())
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
storeId Int @map("store_id")
lang Lang @default(uk)
status Boolean? @default(true)
title String @db.VarChar(384)
slug String? @db.VarChar(384)
motto String? @map("motto")
shortTitle String? @map("short_title")
description String? @db.Text
content String? @db.MediumText
extendedData Json? @map("extended_data") @db.Json
meta Meta? @relation(fields: [metaId], references: [id])
metaId Int? @map("meta_id")
@@unique([storeId, slug, lang])
@@map("store_locale")
}

View File

@@ -0,0 +1,92 @@
enum UserRole {
BANNED
FROZEN
OBSERVER
CUSTOMER
AGENT
USER
POWERUSER
EDITOR
ADMIN
MODERATOR
SUPERVISOR
}
model User {
id Int @id @default(autoincrement())
locale Lang @default(uk)
active Boolean?
name String?
username String? @unique
email String? @unique
emailVerified DateTime? @map("email_verified") @db.Timestamp(3)
password String? @db.VarChar(384)
image String?
// sessions Session[]
/// [UserExtendedDataType]
extendedData Json? @map("extended_data") @db.Json
// orders Order[]
favorites UserFavouriteProduct[]
reviews UserProductReview[]
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
account Account[]
//authenticator Authenticator[]
//session Session[]
@@map("users")
}
// https://stackoverflow.com/questions/72606917/get-custom-attribute-depend-on-pivot-table-in-nest-js-with-prisma-orm
model UserFavouriteProduct {
id Int @id @default(autoincrement())
status Boolean?
informAvailability Boolean? @map("inform_availability")
user User @relation(fields: [userId], references: [id])
userId Int @map("user_id")
productId Int @map("product_id")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
@@unique([userId, productId])
@@map("user_favourite_products")
}
// https://stackoverflow.com/questions/67065859/modeling-a-rating-system-in-prisma
model UserProductReview {
id Int @id @default(autoincrement())
rating Decimal?
body String @db.Text
productId Int @map("product_id")
user User @relation(fields: [userId], references: [id])
userId Int @map("user_id")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
@@unique([userId, productId])
@@map("user_product_reviews")
}
model Account {
id Int @id @default(autoincrement())
userId Int @unique @map("user_id")
role UserRole @default(CUSTOMER)
active Boolean @default(true)
addess String?
type String
provider String
providerAccountId String @map("provider_account_id")
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
refresh_token_expires_in Int?
user User @relation(fields: [userId], references: [id])
@@unique([provider, providerAccountId])
@@index([userId])
@@map("accounts")
}

View File

@@ -0,0 +1,28 @@
import {Prisma, PrismaClient, Store} from '@prisma/client'
import {DefaultArgs} from '@prisma/client/runtime/library'
import {storeSeed} from '@/lib/db/prisma/seed/store'
export type prismaClientType = PrismaClient<
Prisma.PrismaClientOptions,
never,
DefaultArgs
>
const prisma = new PrismaClient()
async function main() {
console.log('!!!!')
console.log(await storeSeed(prisma))
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async e => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})

View File

@@ -0,0 +1,55 @@
import {Store} from '@prisma/client'
import {type prismaClientType} from '@/lib/db/prisma/seed/main'
import {slug} from '@/lib/utils'
const STORE_TITLE = 'Be Well'
const STORE_TITLE_RU = STORE_TITLE
export const storeSeed = async function (prisma: prismaClientType) {
const store: Store = await prisma.store.create({
data: {
slug: slug(STORE_TITLE),
active: true,
image: '/images/logo.svg',
storeLocale: {
create: [
{
title: STORE_TITLE,
lang: 'uk',
motto: 'подбай про себе',
slug: slug(STORE_TITLE, 'uk')
// meta: {
// create: {
// keywords: ['харчові добавки', 'вітаміни'],
// openGraph: {
// create: {
// locale: 'uk_UA'
// }
// }
// }
// }
},
{
title: STORE_TITLE_RU,
lang: 'ru',
motto: 'позаботься о себе',
slug: slug(STORE_TITLE_RU, 'ru')
// meta: {
// create: {
// keywords: ['пищевые добавки', 'витамины'],
// openGraph: {
// create: {
// locale: 'ru_UA'
// }
// }
// }
// }
}
]
}
}
})
return store
}

View File

@@ -0,0 +1,24 @@
import {User} from '@prisma/client'
import * as argon2 from 'argon2'
import type {prismaClientType} from '@/lib/db/prisma/seed/main'
import {hashPassword} from '@/lib/utils'
export const superUserSeed = async function (prisma: prismaClientType) {
// const hashedPassword = await hashPassword('b00st!667')
//
// console.log(hashedPassword, await argon2.verify(hashedPassword, 'b00st!667'))
const user: User = await prisma.user.create({
data: {
locale: 'uk',
active: true,
name: 'Yevhen Odynets',
username: 'sv-yeod',
email: 'yevhen.odynets@ugmail.org',
password: 'hashedPassword'
}
})
return user
}

View File

@@ -0,0 +1,14 @@
SELECT u.id,
a.id AS profileId,
IFNULL(a.role, 'CUSTOMER') AS role,
IF(a.id IS NULL, FALSE, TRUE) AS isOauth,
u.name,
u.username,
u.email,
u.image,
a.provider
FROM users u
LEFT JOIN accounts a
ON a.user_id = u.id
WHERE u.id = ?
LIMIT 1;