/
masterOK
/
FEF
Обзор
Документация
Войти
/
masterOK
/
FEF
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
schema.ts
636 строк
26 KB
masterOK
upload files
10 ноя 2025, 19:52
10 ноя 2025, 19:52
2cb4a49
Код
Авторство
О чём код?
import { sql } from "drizzle-orm"; import { pgTable, text, varchar, integer, boolean, timestamp, decimal, pgEnum, uniqueIndex } from "drizzle-orm/pg-core"; import { relations } from "drizzle-orm"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; export const userRoleEnum = pgEnum("user_role", ["client", "master", "admin"]); export const orderStatusEnum = pgEnum("order_status", ["new", "accepted", "in_progress", "completed", "cancelled"]); export const paymentStatusEnum = pgEnum("payment_status", ["pending", "completed", "failed", "refunded"]); export const authProviderEnum = pgEnum("auth_provider", ["local", "google", "vk"]); export const supportTicketStatusEnum = pgEnum("support_ticket_status", ["open", "in_progress", "resolved", "closed"]); export const serviceDeliveryTypeEnum = pgEnum("service_delivery_type", ["online", "offline"]); export const listingStatusEnum = pgEnum("listing_status", ["draft", "active", "sold", "archived"]); export const listingConditionEnum = pgEnum("listing_condition", ["new", "like_new", "good", "fair", "poor"]); export const categoryTypeEnum = pgEnum("category_type", ["service", "article", "listing"]); export const articleStatusEnum = pgEnum("article_status", ["pending", "approved", "rejected"]); export const consultationTypeEnum = pgEnum("consultation_type", ["video", "phone", "chat"]); export const users = pgTable("users", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), email: text("email").notNull().unique(), passwordHash: text("password_hash"), role: userRoleEnum("role").notNull().default("client"), fullName: text("full_name").notNull(), phone: text("phone"), avatarUrl: text("avatar_url"), city: text("city"), oauthProvider: text("oauth_provider"), oauthId: text("oauth_id"), reputation: integer("reputation").notNull().default(0), latitude: decimal("latitude", { precision: 10, scale: 8 }), longitude: decimal("longitude", { precision: 11, scale: 8 }), createdAt: timestamp("created_at").notNull().default(sql`now()`), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), }); export const masters = pgTable("masters", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), specialization: text("specialization").notNull(), experienceYears: integer("experience_years").notNull().default(0), description: text("description"), hourlyRate: decimal("hourly_rate", { precision: 10, scale: 2 }), isVerified: boolean("is_verified").notNull().default(false), rating: decimal("rating", { precision: 3, scale: 2 }).default("0"), completedOrders: integer("completed_orders").notNull().default(0), ranking: integer("ranking"), topMasterBadge: text("top_master_badge"), availableNow: boolean("available_now").notNull().default(true), latitude: decimal("latitude", { precision: 10, scale: 8 }), longitude: decimal("longitude", { precision: 11, scale: 8 }), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const categories = pgTable("categories", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull(), description: text("description"), icon: text("icon"), type: categoryTypeEnum("type").notNull().default("service"), parentId: varchar("parent_id", { length: 36 }).references((): any => categories.id), isActive: boolean("is_active").notNull().default(true), }); export const services = pgTable("services", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), masterId: varchar("master_id", { length: 36 }).notNull().references(() => masters.id, { onDelete: "cascade" }), categoryId: varchar("category_id", { length: 36 }).notNull().references(() => categories.id), title: text("title").notNull(), description: text("description"), price: decimal("price", { precision: 10, scale: 2 }).notNull(), deliveryType: serviceDeliveryTypeEnum("delivery_type").notNull().default("offline"), durationMinutes: integer("duration_minutes"), isActive: boolean("is_active").notNull().default(true), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const orders = pgTable("orders", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), clientId: varchar("client_id", { length: 36 }).notNull().references(() => users.id), masterId: varchar("master_id", { length: 36 }).notNull().references(() => masters.id), serviceId: varchar("service_id", { length: 36 }).notNull().references(() => services.id), status: orderStatusEnum("status").notNull().default("new"), address: text("address"), scheduledDate: timestamp("scheduled_date").notNull(), totalPrice: decimal("total_price", { precision: 10, scale: 2 }).notNull(), notes: text("notes"), isConsultation: boolean("is_consultation").notNull().default(false), consultationType: consultationTypeEnum("consultation_type"), consultationLink: text("consultation_link"), consultationDuration: integer("consultation_duration"), createdAt: timestamp("created_at").notNull().default(sql`now()`), completedAt: timestamp("completed_at"), }); export const reviews = pgTable("reviews", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), orderId: varchar("order_id", { length: 36 }).notNull().references(() => orders.id).unique(), rating: integer("rating").notNull(), comment: text("comment"), clientId: varchar("client_id", { length: 36 }).notNull().references(() => users.id), masterId: varchar("master_id", { length: 36 }).notNull().references(() => masters.id), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const portfolioItems = pgTable("portfolio_items", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), masterId: varchar("master_id", { length: 36 }).notNull().references(() => masters.id, { onDelete: "cascade" }), title: text("title").notNull(), description: text("description"), beforeImageUrl: text("before_image_url"), afterImageUrl: text("after_image_url").notNull(), categoryId: varchar("category_id", { length: 36 }).references(() => categories.id), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const carBrands = pgTable("car_brands", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull().unique(), logoUrl: text("logo_url"), isPopular: boolean("is_popular").notNull().default(false), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const articles = pgTable("articles", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), title: text("title").notNull(), content: text("content").notNull(), authorId: varchar("author_id", { length: 36 }).notNull().references(() => users.id), category: text("category").notNull(), tags: text("tags").array(), thumbnailUrl: text("thumbnail_url"), images: text("images").array(), carBrandId: varchar("car_brand_id", { length: 36 }).references(() => carBrands.id), isPublished: boolean("is_published").notNull().default(false), isQuestion: boolean("is_question").notNull().default(false), isAnswered: boolean("is_answered").notNull().default(false), acceptedAnswerId: varchar("accepted_answer_id", { length: 36 }).references((): any => articleComments.id), viewCount: integer("view_count").notNull().default(0), status: articleStatusEnum("status").notNull().default("pending"), moderatedBy: varchar("moderated_by", { length: 36 }).references(() => users.id), moderatedAt: timestamp("moderated_at"), rejectionReason: text("rejection_reason"), createdAt: timestamp("created_at").notNull().default(sql`now()`), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), }); export const achievements = pgTable("achievements", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull().unique(), description: text("description").notNull(), icon: text("icon"), points: integer("points").notNull().default(0), criteriaType: text("criteria_type").notNull(), criteriaValue: integer("criteria_value").notNull(), }); export const userAchievements = pgTable("user_achievements", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), achievementId: varchar("achievement_id", { length: 36 }).notNull().references(() => achievements.id), earnedAt: timestamp("earned_at").notNull().default(sql`now()`), }); export const chatMessages = pgTable("chat_messages", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), orderId: varchar("order_id", { length: 36 }).notNull().references(() => orders.id, { onDelete: "cascade" }), senderId: varchar("sender_id", { length: 36 }).notNull().references(() => users.id), receiverId: varchar("receiver_id", { length: 36 }).notNull().references(() => users.id), message: text("message").notNull(), isRead: boolean("is_read").notNull().default(false), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const transactions = pgTable("transactions", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), orderId: varchar("order_id", { length: 36 }).notNull().references(() => orders.id), amount: decimal("amount", { precision: 10, scale: 2 }).notNull(), totalAmount: decimal("total_amount", { precision: 10, scale: 2 }), platformCommission: decimal("platform_commission", { precision: 10, scale: 2 }).notNull().default("0"), masterPayout: decimal("master_payout", { precision: 10, scale: 2 }), commissionRate: decimal("commission_rate", { precision: 5, scale: 2 }).notNull().default("0"), paymentMethod: text("payment_method").notNull(), status: paymentStatusEnum("status").notNull().default("pending"), yukassaPaymentId: text("yukassa_payment_id"), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const authIdentities = pgTable("auth_identities", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), provider: authProviderEnum("provider").notNull(), providerUserId: text("provider_user_id"), passwordHash: text("password_hash"), accessToken: text("access_token"), refreshToken: text("refresh_token"), createdAt: timestamp("created_at").notNull().default(sql`now()`), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), }); export const supportTickets = pgTable("support_tickets", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), subject: text("subject").notNull(), description: text("description").notNull(), status: supportTicketStatusEnum("status").notNull().default("open"), priority: text("priority").notNull().default("medium"), assignedToId: varchar("assigned_to_id", { length: 36 }).references(() => users.id), createdAt: timestamp("created_at").notNull().default(sql`now()`), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), closedAt: timestamp("closed_at"), }); export const supportMessages = pgTable("support_messages", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), ticketId: varchar("ticket_id", { length: 36 }).notNull().references(() => supportTickets.id, { onDelete: "cascade" }), senderId: varchar("sender_id", { length: 36 }).references(() => users.id), message: text("message").notNull(), attachments: text("attachments").array(), isInternal: boolean("is_internal").notNull().default(false), createdAt: timestamp("created_at").notNull().default(sql`now()`), }); export const listings = pgTable("listings", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), ownerId: varchar("owner_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), categoryId: varchar("category_id", { length: 36 }).notNull().references(() => categories.id), title: text("title").notNull(), description: text("description").notNull(), price: decimal("price", { precision: 10, scale: 2 }).notNull(), currency: text("currency").notNull().default("RUB"), condition: listingConditionEnum("condition"), status: listingStatusEnum("status").notNull().default("draft"), location: text("location"), contactPhone: text("contact_phone"), contactEmail: text("contact_email"), images: text("images").array(), autoBrand: text("auto_brand"), autoModel: text("auto_model"), autoYear: integer("auto_year"), autoMileage: integer("auto_mileage"), viewCount: integer("view_count").notNull().default(0), createdAt: timestamp("created_at").notNull().default(sql`now()`), publishedAt: timestamp("published_at"), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), }); export const articleComments = pgTable("article_comments", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), articleId: varchar("article_id", { length: 36 }).notNull().references(() => articles.id, { onDelete: "cascade" }), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), parentId: varchar("parent_id", { length: 36 }).references((): any => articleComments.id), content: text("content").notNull(), isQuestion: boolean("is_question").notNull().default(false), isAnswer: boolean("is_answer").notNull().default(false), upvotes: integer("upvotes").notNull().default(0), createdAt: timestamp("created_at").notNull().default(sql`now()`), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), }); export const articleCommentVotes = pgTable("article_comment_votes", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), commentId: varchar("comment_id", { length: 36 }).notNull().references(() => articleComments.id, { onDelete: "cascade" }), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), createdAt: timestamp("created_at").notNull().default(sql`now()`), }, (table) => ({ uniqueVote: uniqueIndex("unique_comment_user_vote").on(table.commentId, table.userId), })); export const pushSubscriptions = pgTable("push_subscriptions", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), endpoint: text("endpoint").notNull(), p256dh: text("p256dh").notNull(), auth: text("auth").notNull(), createdAt: timestamp("created_at").notNull().default(sql`now()`), }, (table) => ({ uniqueSubscription: uniqueIndex("unique_user_endpoint").on(table.userId, table.endpoint), })); export const usersRelations = relations(users, ({ one, many }) => ({ masterProfile: one(masters, { fields: [users.id], references: [masters.userId], }), ordersAsClient: many(orders, { relationName: "clientOrders" }), reviews: many(reviews), articles: many(articles), userAchievements: many(userAchievements), sentMessages: many(chatMessages, { relationName: "sentMessages" }), receivedMessages: many(chatMessages, { relationName: "receivedMessages" }), authIdentities: many(authIdentities), supportTickets: many(supportTickets), supportMessages: many(supportMessages), articleComments: many(articleComments), listings: many(listings), })); export const mastersRelations = relations(masters, ({ one, many }) => ({ user: one(users, { fields: [masters.userId], references: [users.id], }), services: many(services), orders: many(orders), reviews: many(reviews), })); export const categoriesRelations = relations(categories, ({ one, many }) => ({ parent: one(categories, { fields: [categories.parentId], references: [categories.id], }), subcategories: many(categories), services: many(services), listings: many(listings), })); export const servicesRelations = relations(services, ({ one, many }) => ({ master: one(masters, { fields: [services.masterId], references: [masters.id], }), category: one(categories, { fields: [services.categoryId], references: [categories.id], }), orders: many(orders), })); export const ordersRelations = relations(orders, ({ one, many }) => ({ client: one(users, { fields: [orders.clientId], references: [users.id], relationName: "clientOrders", }), master: one(masters, { fields: [orders.masterId], references: [masters.id], }), service: one(services, { fields: [orders.serviceId], references: [services.id], }), review: one(reviews), messages: many(chatMessages), transaction: one(transactions), })); export const reviewsRelations = relations(reviews, ({ one }) => ({ order: one(orders, { fields: [reviews.orderId], references: [orders.id], }), client: one(users, { fields: [reviews.clientId], references: [users.id], }), master: one(masters, { fields: [reviews.masterId], references: [masters.id], }), })); export const carBrandsRelations = relations(carBrands, ({ many }) => ({ articles: many(articles), })); export const articlesRelations = relations(articles, ({ one, many }) => ({ author: one(users, { fields: [articles.authorId], references: [users.id], }), carBrand: one(carBrands, { fields: [articles.carBrandId], references: [carBrands.id], }), comments: many(articleComments), })); export const userAchievementsRelations = relations(userAchievements, ({ one }) => ({ user: one(users, { fields: [userAchievements.userId], references: [users.id], }), achievement: one(achievements, { fields: [userAchievements.achievementId], references: [achievements.id], }), })); export const chatMessagesRelations = relations(chatMessages, ({ one }) => ({ order: one(orders, { fields: [chatMessages.orderId], references: [orders.id], }), sender: one(users, { fields: [chatMessages.senderId], references: [users.id], relationName: "sentMessages", }), receiver: one(users, { fields: [chatMessages.receiverId], references: [users.id], relationName: "receivedMessages", }), })); export const transactionsRelations = relations(transactions, ({ one }) => ({ order: one(orders, { fields: [transactions.orderId], references: [orders.id], }), })); export const authIdentitiesRelations = relations(authIdentities, ({ one }) => ({ user: one(users, { fields: [authIdentities.userId], references: [users.id], }), })); export const supportTicketsRelations = relations(supportTickets, ({ one, many }) => ({ user: one(users, { fields: [supportTickets.userId], references: [users.id], }), assignedTo: one(users, { fields: [supportTickets.assignedToId], references: [users.id], }), messages: many(supportMessages), })); export const supportMessagesRelations = relations(supportMessages, ({ one }) => ({ ticket: one(supportTickets, { fields: [supportMessages.ticketId], references: [supportTickets.id], }), sender: one(users, { fields: [supportMessages.senderId], references: [users.id], }), })); export const articleCommentsRelations = relations(articleComments, ({ one, many }) => ({ article: one(articles, { fields: [articleComments.articleId], references: [articles.id], }), user: one(users, { fields: [articleComments.userId], references: [users.id], }), parent: one(articleComments, { fields: [articleComments.parentId], references: [articleComments.id], }), replies: many(articleComments), })); export const insertUserSchema = createInsertSchema(users).omit({ id: true, createdAt: true, updatedAt: true, passwordHash: true, }).extend({ password: z.string().min(6, "Пароль должен содержать минимум 6 символов"), }); export const insertMasterSchema = createInsertSchema(masters).omit({ id: true, createdAt: true, rating: true, completedOrders: true, }); export const insertServiceSchema = createInsertSchema(services).omit({ id: true, createdAt: true, }); export const insertOrderSchema = createInsertSchema(orders).omit({ id: true, createdAt: true, completedAt: true, }).extend({ scheduledDate: z.string().or(z.date()).transform((val) => { if (typeof val === 'string') { return new Date(val); } return val; }), }); export const insertReviewSchema = createInsertSchema(reviews).omit({ id: true, createdAt: true, }).extend({ rating: z.number().min(1).max(5, "Рейтинг должен быть от 1 до 5"), }); export const insertPortfolioItemSchema = createInsertSchema(portfolioItems).omit({ id: true, createdAt: true, }); export const insertArticleSchema = createInsertSchema(articles).omit({ id: true, createdAt: true, updatedAt: true, status: true, moderatedBy: true, moderatedAt: true, rejectionReason: true, viewCount: true, }); export const insertAchievementSchema = createInsertSchema(achievements).omit({ id: true, }); export const insertChatMessageSchema = createInsertSchema(chatMessages).omit({ id: true, createdAt: true, isRead: true, }); export const insertTransactionSchema = createInsertSchema(transactions).omit({ id: true, createdAt: true, }); export const insertAuthIdentitySchema = createInsertSchema(authIdentities).omit({ id: true, createdAt: true, updatedAt: true, }); export const insertSupportTicketSchema = createInsertSchema(supportTickets).omit({ id: true, createdAt: true, updatedAt: true, closedAt: true, }); export const insertSupportMessageSchema = createInsertSchema(supportMessages).omit({ id: true, createdAt: true, }); export const insertArticleCommentSchema = createInsertSchema(articleComments).omit({ id: true, createdAt: true, updatedAt: true, upvotes: true, }); export const insertListingSchema = createInsertSchema(listings).omit({ id: true, createdAt: true, updatedAt: true, publishedAt: true, viewCount: true, }); export const insertPushSubscriptionSchema = createInsertSchema(pushSubscriptions).omit({ id: true, createdAt: true, }); export type InsertUser = z.infer<typeof insertUserSchema>; export type User = typeof users.$inferSelect; export type InsertMaster = z.infer<typeof insertMasterSchema>; export type Master = typeof masters.$inferSelect; export type InsertService = z.infer<typeof insertServiceSchema>; export type Service = typeof services.$inferSelect; export type InsertOrder = z.infer<typeof insertOrderSchema>; export type Order = typeof orders.$inferSelect; export type InsertReview = z.infer<typeof insertReviewSchema>; export type Review = typeof reviews.$inferSelect; export type InsertPortfolioItem = z.infer<typeof insertPortfolioItemSchema>; export type PortfolioItem = typeof portfolioItems.$inferSelect; export type CarBrand = typeof carBrands.$inferSelect; export type InsertArticle = z.infer<typeof insertArticleSchema>; export type Article = typeof articles.$inferSelect; export type InsertAchievement = z.infer<typeof insertAchievementSchema>; export type Achievement = typeof achievements.$inferSelect; export type Category = typeof categories.$inferSelect; export type UserAchievement = typeof userAchievements.$inferSelect; export type ChatMessage = typeof chatMessages.$inferSelect; export type InsertChatMessage = z.infer<typeof insertChatMessageSchema>; export type Transaction = typeof transactions.$inferSelect; export type InsertTransaction = z.infer<typeof insertTransactionSchema>; export type AuthIdentity = typeof authIdentities.$inferSelect; export type SupportTicket = typeof supportTickets.$inferSelect; export type SupportMessage = typeof supportMessages.$inferSelect; export type ArticleComment = typeof articleComments.$inferSelect; export type InsertAuthIdentity = z.infer<typeof insertAuthIdentitySchema>; export type InsertSupportTicket = z.infer<typeof insertSupportTicketSchema>; export type InsertSupportMessage = z.infer<typeof insertSupportMessageSchema>; export type InsertArticleComment = z.infer<typeof insertArticleCommentSchema>; export type Listing = typeof listings.$inferSelect; export type InsertListing = z.infer<typeof insertListingSchema>; export type PushSubscription = typeof pushSubscriptions.$inferSelect; export type InsertPushSubscription = z.infer<typeof insertPushSubscriptionSchema>; // Blog posts export const blogPosts = pgTable("blog_posts", { id: varchar("id", { length: 36 }).primaryKey().default(sql`gen_random_uuid()`), title: varchar("title", { length: 255 }).notNull(), slug: varchar("slug", { length: 255 }).notNull().unique(), content: text("content").notNull(), excerpt: text("excerpt"), category: varchar("category", { length: 100 }), authorId: varchar("author_id", { length: 36 }).notNull().references(() => users.id, { onDelete: "cascade" }), published: boolean("published").notNull().default(false), publishedAt: timestamp("published_at"), createdAt: timestamp("created_at").notNull().default(sql`now()`), updatedAt: timestamp("updated_at").notNull().default(sql`now()`), }); export const insertBlogPostSchema = createInsertSchema(blogPosts).omit({ id: true, createdAt: true, updatedAt: true }); export type InsertBlogPost = z.infer<typeof insertBlogPostSchema>; export type BlogPost = typeof blogPosts.$inferSelect; export type BlogPostWithAuthor = BlogPost & { author: User; }; export type MasterProfile = Master & { user: User; services: Service[]; reviews: Array<Review & { client: User }>; portfolioItems: PortfolioItem[]; };