feat(core): scaffold account manager platform
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import * as schema from "./schema";
|
||||
|
||||
export function createDatabase(databaseUrl: string) {
|
||||
const client = postgres(databaseUrl, { max: 10 });
|
||||
return {
|
||||
client,
|
||||
db: drizzle(client, { schema }),
|
||||
};
|
||||
}
|
||||
|
||||
export * from "./schema";
|
||||
@@ -0,0 +1,213 @@
|
||||
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<Record<string, unknown>>(),
|
||||
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<Record<string, unknown>>().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`),
|
||||
],
|
||||
);
|
||||
Reference in New Issue
Block a user