waveform/db/schema.ts
Chneemann 4b9256e570
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 1m3s
feat(db): add categories table and update channels relations
2026-09-12 18:35:33 +02:00

287 lines
9.7 KiB
TypeScript

/**
* @file db/schema.ts
* @description Database schema definition using Drizzle ORM, containing enums, table definitions, relations, and TypeScript type exports for users, servers, members, channels, categories, messages, conversations, direct messages and friendships.
*/
import { relations } from "drizzle-orm";
import {
boolean,
pgEnum,
pgTable,
text,
timestamp,
uuid,
varchar,
} from "drizzle-orm/pg-core";
// ==========================================
// Enums
// ==========================================
/** Enum representing member access roles within a server. */
export const roleEnum = pgEnum("role", ["OWNER", "ADMIN", "MEMBER"]);
/** Enum representing online status of a user. */
export const userStatusEnum = pgEnum("user_status", [
"ONLINE",
"OFFLINE",
"IDLE",
"DND",
]);
/** Enum representing friendship status between two users. */
export const friendshipStatusEnum = pgEnum("friendship_status", [
"PENDING",
"ACCEPTED",
"BLOCKED",
]);
// ==========================================
// Tables
// ==========================================
/** Database table definition for application users. */
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
username: text("username").notNull(),
email: varchar("email", { length: 255 }).notNull().unique(),
password: text("password").notNull(),
color: varchar("color", { length: 50 }).default("bg-indigo-500").notNull(),
status: userStatusEnum("status").default("OFFLINE").notNull(),
lastSeenAt: timestamp("last_seen_at").defaultNow().notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
/** Database table definition for chat servers. */
export const servers = pgTable("servers", {
id: uuid("id").primaryKey().defaultRandom(),
name: varchar("name", { length: 32 }).notNull(),
color: varchar("color", { length: 50 }).default("bg-indigo-500").notNull(),
inviteCode: varchar("invite_code", { length: 20 }).notNull().unique(),
ownerId: uuid("owner_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
/** Database table definition linking users to servers with specific roles. */
export const members = pgTable("members", {
id: uuid("id").primaryKey().defaultRandom(),
role: roleEnum("role").default("MEMBER").notNull(),
userId: uuid("user_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
serverId: uuid("server_id")
.references(() => servers.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
/** Database table definition for text channels within servers. */
export const channels = pgTable("channels", {
id: uuid("id").primaryKey().defaultRandom(),
name: varchar("name", { length: 32 }).notNull(),
serverId: uuid("server_id")
.references(() => servers.id, { onDelete: "cascade" })
.notNull(),
categoryId: uuid("category_id").references(() => categories.id, {
onDelete: "set null",
}),
isDefault: boolean("is_default").default(false).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
/** Database table definition for channel categories within servers. */
export const categories = pgTable("categories", {
id: uuid("id").primaryKey().defaultRandom(),
name: varchar("name", { length: 32 }).notNull(),
serverId: uuid("server_id")
.references(() => servers.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
/** Database table definition for chat messages sent within channels by members. */
export const messages = pgTable("messages", {
id: uuid("id").primaryKey().defaultRandom(),
content: text("content").notNull(),
channelId: uuid("channel_id")
.references(() => channels.id, { onDelete: "cascade" })
.notNull(),
memberId: uuid("member_id")
.references(() => members.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
/** Database table definition for friendships / friend requests between users. */
export const friendships = pgTable("friendships", {
id: uuid("id").primaryKey().defaultRandom(),
status: friendshipStatusEnum("status").default("PENDING").notNull(),
senderId: uuid("sender_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
receiverId: uuid("receiver_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
/** Database table definition for direct message conversations between two users. */
export const conversations = pgTable("conversations", {
id: uuid("id").primaryKey().defaultRandom(),
userOneId: uuid("user_one_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
userTwoId: uuid("user_two_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
/** Database table definition for direct messages sent within a conversation. */
export const directMessages = pgTable("direct_messages", {
id: uuid("id").primaryKey().defaultRandom(),
content: text("content").notNull(),
conversationId: uuid("conversation_id")
.references(() => conversations.id, { onDelete: "cascade" })
.notNull(),
senderId: uuid("sender_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
// ==========================================
// Drizzle Relations
// ==========================================
/** Relational definitions for the users table. */
export const usersRelations = relations(users, ({ many }) => ({
memberships: many(members),
sentFriendRequests: many(friendships, { relationName: "sentFriendships" }),
receivedFriendRequests: many(friendships, {
relationName: "receivedFriendships",
}),
conversationsInitiated: many(conversations, {
relationName: "userOneConversations",
}),
conversationsReceived: many(conversations, {
relationName: "userTwoConversations",
}),
directMessages: many(directMessages),
}));
/** Relational definitions for the servers table. */
export const serversRelations = relations(servers, ({ many }) => ({
channels: many(channels),
categories: many(categories),
members: many(members),
}));
/** Relational definitions for the members table. */
export const membersRelations = relations(members, ({ one, many }) => ({
user: one(users, { fields: [members.userId], references: [users.id] }),
server: one(servers, {
fields: [members.serverId],
references: [servers.id],
}),
messages: many(messages),
}));
/** Relational definitions for the channels table. */
export const channelsRelations = relations(channels, ({ one, many }) => ({
server: one(servers, {
fields: [channels.serverId],
references: [servers.id],
}),
category: one(categories, {
fields: [channels.categoryId],
references: [categories.id],
}),
messages: many(messages),
}));
/** Relational definitions for the categories table. */
export const categoriesRelations = relations(categories, ({ one, many }) => ({
server: one(servers, {
fields: [categories.serverId],
references: [servers.id],
}),
channels: many(channels),
}));
/** Relational definitions for the messages table. */
export const messagesRelations = relations(messages, ({ one }) => ({
channel: one(channels, {
fields: [messages.channelId],
references: [channels.id],
}),
member: one(members, {
fields: [messages.memberId],
references: [members.id],
}),
}));
/** Relational definitions for the friendships table. */
export const friendshipsRelations = relations(friendships, ({ one }) => ({
sender: one(users, {
fields: [friendships.senderId],
references: [users.id],
relationName: "sentFriendships",
}),
receiver: one(users, {
fields: [friendships.receiverId],
references: [users.id],
relationName: "receivedFriendships",
}),
}));
/** Relational definitions for the conversations table. */
export const conversationsRelations = relations(
conversations,
({ one, many }) => ({
userOne: one(users, {
fields: [conversations.userOneId],
references: [users.id],
relationName: "userOneConversations",
}),
userTwo: one(users, {
fields: [conversations.userTwoId],
references: [users.id],
relationName: "userTwoConversations",
}),
directMessages: many(directMessages),
}),
);
/** Relational definitions for the directMessages table. */
export const directMessagesRelations = relations(directMessages, ({ one }) => ({
conversation: one(conversations, {
fields: [directMessages.conversationId],
references: [conversations.id],
}),
sender: one(users, {
fields: [directMessages.senderId],
references: [users.id],
}),
}));
// ==========================================
// Type Exports
// ==========================================
export type User = typeof users.$inferSelect;
export type Server = typeof servers.$inferSelect;
export type Member = typeof members.$inferSelect;
export type Channel = typeof channels.$inferSelect;
export type Category = typeof categories.$inferSelect;
export type Message = typeof messages.$inferSelect;
export type UserStatus = (typeof userStatusEnum.enumValues)[number];
export type Friendship = typeof friendships.$inferSelect;
export type FriendshipStatus = (typeof friendshipStatusEnum.enumValues)[number];
export type Conversation = typeof conversations.$inferSelect;
export type DirectMessage = typeof directMessages.$inferSelect;