import { sql } from "drizzle-orm"; import { boolean, index, inet, jsonb, pgEnum, pgTable, text, timestamp, uniqueIndex, uuid, varchar, } from "drizzle-orm/pg-core"; function timestamps() { return { createdAt: timestamp("created_at", { withTimezone: true, mode: "date", precision: 3 }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date", precision: 3 }) .notNull() .defaultNow(), }; } function createdAt() { return timestamp("created_at", { withTimezone: true, mode: "date", precision: 3 }) .notNull() .defaultNow(); } export const minecraftValidationStatus = pgEnum("minecraft_validation_status", [ "verified", "user_confirmed", ]); export const ipObservationSource = pgEnum("ip_observation_source", ["web", "game"]); export const ipClassification = pgEnum("ip_classification", [ "unknown", "clear", "vpn", "proxy", "hosting", "tor", ]); export const users = pgTable( "users", { id: uuid("id").primaryKey().defaultRandom(), discordUserId: text("discord_user_id").notNull(), discordUsername: text("discord_username").notNull(), discordGlobalName: text("discord_global_name"), firstName: text("first_name"), onboardingCompletedAt: timestamp("onboarding_completed_at", { withTimezone: true, mode: "date", precision: 3, }), ...timestamps(), }, (table) => [uniqueIndex("users_discord_user_id_uidx").on(table.discordUserId)], ); export const minecraftAccounts = pgTable( "minecraft_accounts", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), minecraftUuid: varchar("minecraft_uuid", { length: 32 }), username: varchar("username", { length: 16 }).notNull(), validationStatus: minecraftValidationStatus("validation_status").notNull(), isPrimary: boolean("is_primary").notNull().default(false), lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true, mode: "date", precision: 3 }), deletedAt: timestamp("deleted_at", { withTimezone: true, mode: "date", precision: 3 }), ...timestamps(), }, (table) => [ index("minecraft_accounts_user_idx").on(table.userId), uniqueIndex("minecraft_accounts_active_uuid_uidx") .on(table.minecraftUuid) .where(sql`${table.deletedAt} is null and ${table.minecraftUuid} is not null`), uniqueIndex("minecraft_accounts_active_username_uidx") .on(sql`lower(${table.username})`) .where(sql`${table.deletedAt} is null`), uniqueIndex("minecraft_accounts_one_primary_per_user_uidx") .on(table.userId) .where(sql`${table.isPrimary} = true and ${table.deletedAt} is null`), ], ); export const loginCodes = pgTable( "login_codes", { id: uuid("id").primaryKey().defaultRandom(), tokenHash: text("token_hash").notNull(), discordUserId: text("discord_user_id").notNull(), discordUsername: text("discord_username").notNull(), discordGlobalName: text("discord_global_name"), expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date", precision: 3 }).notNull(), consumedAt: timestamp("consumed_at", { withTimezone: true, mode: "date", precision: 3 }), createdAt: createdAt(), }, (table) => [ uniqueIndex("login_codes_token_hash_uidx").on(table.tokenHash), index("login_codes_discord_user_idx").on(table.discordUserId), index("login_codes_expires_idx").on(table.expiresAt), ], ); export const sessions = pgTable( "sessions", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), tokenHash: text("token_hash").notNull(), expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date", precision: 3 }).notNull(), lastSeenAt: timestamp("last_seen_at", { withTimezone: true, mode: "date", precision: 3 }) .notNull() .defaultNow(), revokedAt: timestamp("revoked_at", { withTimezone: true, mode: "date", precision: 3 }), createdAt: createdAt(), }, (table) => [ uniqueIndex("sessions_token_hash_uidx").on(table.tokenHash), index("sessions_user_idx").on(table.userId), index("sessions_expires_idx").on(table.expiresAt), ], ); export const appSettings = pgTable("app_settings", { id: text("id").primaryKey().default("default"), discordGuildId: text("discord_guild_id"), registrationMessage: text("registration_message") .notNull() .default("Please register your Minecraft account before joining."), ...timestamps(), }); export const ipIntelligence = pgTable("ip_intelligence", { ipAddress: inet("ip_address").primaryKey(), classification: ipClassification("classification").notNull().default("unknown"), provider: text("provider"), rawResponse: jsonb("raw_response").$type>(), checkedAt: timestamp("checked_at", { withTimezone: true, mode: "date", precision: 3 }), expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date", precision: 3 }), ...timestamps(), }); export const ipObservations = pgTable( "ip_observations", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id").references(() => users.id, { onDelete: "set null" }), minecraftAccountId: uuid("minecraft_account_id").references(() => minecraftAccounts.id, { onDelete: "set null", }), source: ipObservationSource("source").notNull(), ipAddress: inet("ip_address").notNull(), minecraftUuid: varchar("minecraft_uuid", { length: 32 }), username: varchar("username", { length: 16 }), classification: ipClassification("classification").notNull().default("unknown"), observedAt: timestamp("observed_at", { withTimezone: true, mode: "date", precision: 3 }) .notNull() .defaultNow(), }, (table) => [ index("ip_observations_user_observed_idx").on(table.userId, table.observedAt), index("ip_observations_account_observed_idx").on(table.minecraftAccountId, table.observedAt), ], ); export const pluginCredentials = pgTable( "plugin_credentials", { id: uuid("id").primaryKey().defaultRandom(), serverId: text("server_id").notNull(), secretHash: text("secret_hash").notNull(), revokedAt: timestamp("revoked_at", { withTimezone: true, mode: "date", precision: 3 }), ...timestamps(), }, (table) => [uniqueIndex("plugin_credentials_server_id_uidx").on(table.serverId)], ); export const events = pgTable( "events", { id: uuid("id").primaryKey(), specVersion: text("spec_version").notNull().default("1.0"), source: text("source").notNull(), type: text("type").notNull(), subject: text("subject"), time: timestamp("time", { withTimezone: true, mode: "date", precision: 3 }).notNull(), dataContentType: text("data_content_type").notNull().default("application/json"), dataSchema: text("data_schema"), data: jsonb("data").$type>().notNull(), actorUserId: uuid("actor_user_id").references(() => users.id, { onDelete: "set null" }), ipAddress: inet("ip_address"), correlationId: uuid("correlation_id"), publishedAt: timestamp("published_at", { withTimezone: true, mode: "date", precision: 3 }), createdAt: createdAt(), }, (table) => [ index("events_time_idx").on(table.time), index("events_type_time_idx").on(table.type, table.time), index("events_subject_time_idx").on(table.subject, table.time), index("events_unpublished_idx").on(table.createdAt).where(sql`${table.publishedAt} is null`), ], );