import { sqliteTable, text, integer, primaryKey, foreignKey, real, unique, index, uniqueIndex } from 'drizzle-orm/sqlite-core'; import { sql } from 'drizzle-orm'; export const users = sqliteTable('users', { id: text('id').primaryKey(), username: text('username').unique().notNull(), displayName: text('display_name'), passwordHash: text('password_hash').notNull(), avatar: text('avatar'), status: text('status').default('offline'), customStatus: text('custom_status'), isAdmin: integer('is_admin').default(0), homeInstance: text('home_instance'), homeUserId: text('home_user_id'), replicatedInstances: text('replicated_instances').default('[]'), banner: text('banner'), accentColor: text('accent_color'), avatarColor: text('avatar_color'), bio: text('bio'), isDeleted: integer('is_deleted').default(0), discoverable: integer('discoverable').default(1), profileUpdatedAt: integer('profile_updated_at'), passwordChangedAt: integer('password_changed_at'), showActivity: integer('show_activity').notNull().default(1), federationRegistryUpdatedAt: integer('federation_registry_updated_at').default(0), federationHealPending: integer('federation_heal_pending').default(0), federationHomeOrphaned: integer('federation_home_orphaned').default(0), createdAt: integer('created_at').notNull(), }); export const spaces = sqliteTable('spaces', { id: text('id').primaryKey(), name: text('name').notNull(), icon: text('icon'), banner: text('banner'), avatarColor: text('avatar_color'), ownerId: text('owner_id').notNull().references(() => users.id), inviteCode: text('invite_code').unique(), visibility: text('visibility').default('private'), description: text('description'), createdAt: integer('created_at').notNull(), }); export const spaceMembers = sqliteTable('space_members', { spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }), userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }), nickname: text('nickname'), joinedAt: integer('joined_at').notNull(), }, (table) => ({ pk: primaryKey({ columns: [table.spaceId, table.userId] }), userIdx: index('idx_space_members_user_id').on(table.userId), })); export const channelCategories = sqliteTable('channel_categories', { id: text('id').primaryKey(), spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }), name: text('name').notNull(), position: integer('position').default(0), createdAt: integer('created_at').notNull(), }, (table) => ({ spaceIdx: index('idx_channel_categories_space_id').on(table.spaceId), })); export const channels = sqliteTable('channels', { id: text('id').primaryKey(), spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }), name: text('name').notNull(), type: text('type').notNull(), topic: text('topic'), position: integer('position').default(0), categoryId: text('category_id'), createdAt: integer('created_at').notNull(), }, (table) => ({ spaceIdx: index('idx_channels_space_id').on(table.spaceId), })); export const messages = sqliteTable('messages', { id: text('id').primaryKey(), channelId: text('channel_id').notNull().references(() => channels.id, { onDelete: 'cascade' }), userId: text('user_id').notNull().references(() => users.id), replyToId: text('reply_to_id'), content: text('content'), editedAt: integer('edited_at'), createdAt: integer('created_at').notNull(), }, (table) => ({ replyToFk: foreignKey({ columns: [table.replyToId], foreignColumns: [table.id], }).onDelete('set null'), channelIdx: index('idx_messages_channel_id').on(table.channelId), userIdx: index('idx_messages_user_id').on(table.userId), })); export const attachments = sqliteTable('attachments', { id: text('id').primaryKey(), messageId: text('message_id').references(() => messages.id, { onDelete: 'cascade' }), dmMessageId: text('dm_message_id').references(() => dmMessages.id, { onDelete: 'cascade' }), uploaderId: text('uploader_id'), filename: text('filename').notNull(), originalName: text('original_name').notNull(), mimetype: text('mimetype').notNull(), size: integer('size').notNull(), thumbnailFilename: text('thumbnail_filename'), width: integer('width'), height: integer('height'), duration: real('duration'), // Tri-state web-playability for video attachments: 1 = decodable in a // browser