// src/server/entities/schema.ts
import { createFunctionSchema } from "@spfn/core/db";
var authSchema = createFunctionSchema("@spfn/auth");

// src/server/entities/users.ts
import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
import { sql } from "drizzle-orm";

// src/server/entities/roles.ts
import { text, boolean, integer, index } from "drizzle-orm/pg-core";
import { id, timestamps } from "@spfn/core/db";
var roles = authSchema.table(
  "roles",
  {
    // Primary key
    id: id(),
    // Role identifier (used in code, e.g., 'admin', 'editor')
    // Must be unique, lowercase, kebab-case recommended
    name: text("name").notNull().unique(),
    // Display name for UI (e.g., 'Administrator', 'Content Editor')
    displayName: text("display_name").notNull(),
    // Role description
    description: text("description"),
    // Built-in role flag
    // true: Core package roles (user, admin, superadmin) - cannot be deleted
    // false: Custom or preset roles - can be deleted
    isBuiltin: boolean("is_builtin").notNull().default(false),
    // System role flag
    // true: Defined in code (builtin or preset) - deletion restricted
    // false: Runtime created custom role - fully manageable
    isSystem: boolean("is_system").notNull().default(false),
    // Active status
    // false: Deactivated role (users cannot be assigned)
    isActive: boolean("is_active").notNull().default(true),
    // Priority level (higher = more privileged)
    // superadmin: 100, admin: 80, user: 10
    // Used for role hierarchy and conflict resolution
    priority: integer("priority").notNull().default(10),
    ...timestamps()
  },
  (table) => [
    index("roles_name_idx").on(table.name),
    index("roles_is_system_idx").on(table.isSystem),
    index("roles_is_active_idx").on(table.isActive),
    index("roles_is_builtin_idx").on(table.isBuiltin),
    index("roles_priority_idx").on(table.priority)
  ]
);

// src/server/entities/users.ts
var users = authSchema.table(
  "users",
  {
    // Identity
    id: id2(),
    // Email address (unique identifier)
    // Used for: login, password reset, notifications
    email: text2("email").unique(),
    // Phone number in E.164 international format
    // Format: +[country code][number] (e.g., +821012345678)
    // Used for: SMS login, 2FA, notifications
    phone: text2("phone").unique(),
    // Authentication
    // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)
    // Nullable to support OAuth-only accounts
    passwordHash: text2("password_hash"),
    // Force password change on next login
    // Use cases: initial setup, security breach, policy violation
    passwordChangeRequired: boolean2("password_change_required").notNull().default(false),
    // Authorization (Role-Based Access Control)
    // Foreign key to roles table
    // References built-in roles: user (default), admin, superadmin
    // Can also reference custom roles created at runtime
    roleId: bigint("role_id", { mode: "number" }).references(() => roles.id).notNull(),
    // Account status
    // - active: Normal operation (default)
    // - inactive: Deactivated (user request, dormant)
    // - suspended: Locked (security incident, ToS violation)
    status: text2(
      "status",
      {
        enum: ["active", "inactive", "suspended"]
      }
    ).notNull().default("active"),
    // Verification timestamps
    // null = unverified, timestamp = verified at this time
    // Email verification (via verification code or magic link)
    emailVerifiedAt: timestamp("email_verified_at", { withTimezone: true }),
    // Phone verification (via SMS OTP)
    phoneVerifiedAt: timestamp("phone_verified_at", { withTimezone: true }),
    // Metadata
    // Last successful login timestamp
    // Used for: security auditing, dormant account detection
    lastLoginAt: timestamp("last_login_at", { withTimezone: true }),
    ...timestamps2()
  },
  (table) => [
    // Database constraints
    // Ensure at least one identifier exists (email OR phone)
    check(
      "email_or_phone_check",
      sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`
    ),
    // Indexes for query optimization
    index2("users_email_idx").on(table.email),
    index2("users_phone_idx").on(table.phone),
    index2("users_status_idx").on(table.status),
    index2("users_role_id_idx").on(table.roleId)
  ]
);

// src/server/entities/user-social-accounts.ts
import { text as text3, timestamp as timestamp2, uniqueIndex } from "drizzle-orm/pg-core";
import { id as id3, timestamps as timestamps3, foreignKey } from "@spfn/core/db";
var userSocialAccounts = authSchema.table(
  "user_social_accounts",
  {
    id: id3(),
    // Foreign key to users
    userId: foreignKey("user", () => users.id),
    // Provider info
    provider: text3(
      "provider",
      {
        enum: ["google", "github", "kakao", "naver"]
      }
    ).notNull(),
    providerUserId: text3("provider_user_id").notNull(),
    providerEmail: text3("provider_email"),
    // OAuth tokens (encrypted in production)
    accessToken: text3("access_token"),
    refreshToken: text3("refresh_token"),
    tokenExpiresAt: timestamp2("token_expires_at", { withTimezone: true }),
    ...timestamps3()
  },
  (table) => [
    // Unique constraint: one provider account per provider
    uniqueIndex("provider_user_unique_idx").on(table.provider, table.providerUserId)
  ]
);

// src/server/entities/user-public-keys.ts
import { text as text4, timestamp as timestamp3, boolean as boolean3, index as index3 } from "drizzle-orm/pg-core";
import { id as id4, foreignKey as foreignKey2 } from "@spfn/core/db";
var userPublicKeys = authSchema.table(
  "user_public_keys",
  {
    id: id4(),
    // User reference
    userId: foreignKey2("user", () => users.id),
    // Key identification (client-generated UUID)
    keyId: text4("key_id").notNull().unique(),
    // Public key in Base64-encoded DER format (SPKI)
    publicKey: text4("public_key").notNull(),
    // Algorithm used (ES256 recommended, RS256 fallback)
    algorithm: text4("algorithm", {
      enum: ["ES256", "RS256"]
    }).notNull().default("ES256"),
    // Key fingerprint (SHA-256 hash for quick identification)
    fingerprint: text4("fingerprint").notNull(),
    // Key status
    isActive: boolean3("is_active").notNull().default(true),
    // Timestamps
    createdAt: timestamp3("created_at", { mode: "date", withTimezone: true }).notNull().defaultNow(),
    lastUsedAt: timestamp3("last_used_at", { mode: "date", withTimezone: true }),
    expiresAt: timestamp3("expires_at", { mode: "date", withTimezone: true }),
    // Revocation
    revokedAt: timestamp3("revoked_at", { mode: "date", withTimezone: true }),
    revokedReason: text4("revoked_reason")
  },
  (table) => [
    index3("user_public_keys_user_id_idx").on(table.userId),
    index3("user_public_keys_key_id_idx").on(table.keyId),
    index3("user_public_keys_active_idx").on(table.isActive),
    index3("user_public_keys_fingerprint_idx").on(table.fingerprint)
  ]
);

// src/server/entities/verification-codes.ts
import { text as text5, timestamp as timestamp4, index as index4 } from "drizzle-orm/pg-core";
import { id as id5, timestamps as timestamps4 } from "@spfn/core/db";
var verificationCodes = authSchema.table(
  "verification_codes",
  {
    id: id5(),
    // Target (email or phone)
    target: text5("target").notNull(),
    // Email address or E.164 phone number
    targetType: text5(
      "target_type",
      {
        enum: ["email", "phone"]
      }
    ).notNull(),
    // Code
    code: text5("code").notNull(),
    // 6-digit code by default (configurable)
    // Purpose
    purpose: text5(
      "purpose",
      {
        enum: ["registration", "login", "password_reset", "email_change", "phone_change"]
      }
    ).notNull(),
    // Expiry
    expiresAt: timestamp4("expires_at", { withTimezone: true }).notNull(),
    // Usage tracking
    usedAt: timestamp4("used_at", { withTimezone: true }),
    attempts: text5("attempts").notNull().default("0"),
    // Track failed verification attempts
    ...timestamps4()
  },
  (table) => [
    // Index for quick lookup by target and purpose
    index4("target_purpose_idx").on(table.target, table.purpose, table.expiresAt)
  ]
);

// src/server/entities/invitations.ts
import { text as text6, timestamp as timestamp5, bigint as bigint2, index as index5, jsonb } from "drizzle-orm/pg-core";
import { id as id6, timestamps as timestamps5 } from "@spfn/core/db";
var invitations = authSchema.table(
  "user_invitations",
  {
    // Primary key
    id: id6(),
    // Target email address for the invitation
    // Will become the user's email upon acceptance
    email: text6("email").notNull(),
    // Unique invitation token (UUID v4)
    // Used in invitation URL: /auth/invite/{token}
    // Single-use token that expires after acceptance
    token: text6("token").notNull().unique(),
    // Role to be assigned when invitation is accepted
    // Foreign key to roles table
    roleId: bigint2("role_id", { mode: "number" }).references(() => roles.id).notNull(),
    // User who created this invitation
    // Foreign key to users table
    // Used for: audit trail, permission checks
    invitedBy: bigint2("invited_by", { mode: "number" }).references(() => users.id).notNull(),
    // Invitation status
    // - pending: Invitation sent, awaiting acceptance
    // - accepted: User accepted and account created
    // - expired: Invitation expired (automatic)
    // - cancelled: Invitation cancelled by admin
    status: text6(
      "status",
      {
        enum: ["pending", "accepted", "expired", "cancelled"]
      }
    ).notNull().default("pending"),
    // Expiration timestamp (default: 7 days from creation)
    // Invitation cannot be accepted after this time
    // Background job should update status to 'expired'
    expiresAt: timestamp5("expires_at", { withTimezone: true }).notNull(),
    // Timestamp when invitation was accepted
    // null = not yet accepted
    // Used for: audit trail, analytics
    acceptedAt: timestamp5("accepted_at", { withTimezone: true }),
    // Timestamp when invitation was cancelled
    // null = not cancelled
    // Used for: audit trail
    cancelledAt: timestamp5("cancelled_at", { withTimezone: true }),
    // Additional metadata (JSONB)
    // Use cases:
    // - Custom welcome message
    // - Onboarding instructions
    // - Team/department assignment
    // - Custom fields for app-specific data
    // Example: { message: "Welcome!", department: "Engineering" }
    metadata: jsonb("metadata"),
    ...timestamps5()
  },
  (table) => [
    // Indexes for query optimization
    index5("invitations_token_idx").on(table.token),
    index5("invitations_email_idx").on(table.email),
    index5("invitations_status_idx").on(table.status),
    index5("invitations_invited_by_idx").on(table.invitedBy),
    index5("invitations_expires_at_idx").on(table.expiresAt),
    // For cleanup jobs
    index5("invitations_role_id_idx").on(table.roleId)
  ]
);

// src/server/entities/permissions.ts
import { text as text7, boolean as boolean4, index as index6 } from "drizzle-orm/pg-core";
import { id as id7, timestamps as timestamps6 } from "@spfn/core/db";
var permissions = authSchema.table(
  "permissions",
  {
    // Primary key
    id: id7(),
    // Permission identifier (e.g., 'user:delete', 'post:publish')
    // Format: resource:action or namespace:resource:action
    // Must be unique
    name: text7("name").notNull().unique(),
    // Display name for UI
    displayName: text7("display_name").notNull(),
    // Permission description
    description: text7("description"),
    // Category for grouping (e.g., 'user', 'post', 'admin', 'system')
    category: text7("category"),
    // Built-in permission flag
    // true: Core package permissions - cannot be deleted
    // false: Custom or preset permissions
    isBuiltin: boolean4("is_builtin").notNull().default(false),
    // System permission flag
    // true: Defined in code (builtin or preset)
    // false: Runtime created custom permission
    isSystem: boolean4("is_system").notNull().default(false),
    // Active status
    // false: Deactivated permission (not enforced)
    isActive: boolean4("is_active").notNull().default(true),
    ...timestamps6()
  },
  (table) => [
    index6("permissions_name_idx").on(table.name),
    index6("permissions_category_idx").on(table.category),
    index6("permissions_is_system_idx").on(table.isSystem),
    index6("permissions_is_active_idx").on(table.isActive),
    index6("permissions_is_builtin_idx").on(table.isBuiltin)
  ]
);

// src/server/entities/role-permissions.ts
import { bigint as bigint3, index as index7, unique } from "drizzle-orm/pg-core";
import { id as id8, timestamps as timestamps7 } from "@spfn/core/db";
var rolePermissions = authSchema.table(
  "role_permissions",
  {
    // Primary key
    id: id8(),
    // Foreign key to roles table
    roleId: bigint3("role_id", { mode: "number" }).notNull().references(() => roles.id, { onDelete: "cascade" }),
    // Foreign key to permissions table
    permissionId: bigint3("permission_id", { mode: "number" }).notNull().references(() => permissions.id, { onDelete: "cascade" }),
    ...timestamps7()
  },
  (table) => [
    // Indexes for query performance
    index7("role_permissions_role_id_idx").on(table.roleId),
    index7("role_permissions_permission_id_idx").on(table.permissionId),
    // Unique constraint: one role-permission pair only
    unique("role_permissions_unique").on(table.roleId, table.permissionId)
  ]
);

// src/server/entities/user-permissions.ts
import { bigint as bigint4, boolean as boolean5, text as text8, timestamp as timestamp6, index as index8, unique as unique2 } from "drizzle-orm/pg-core";
import { id as id9, timestamps as timestamps8 } from "@spfn/core/db";
var userPermissions = authSchema.table(
  "user_permissions",
  {
    // Primary key
    id: id9(),
    // Foreign key to users table
    userId: bigint4("user_id", { mode: "number" }).notNull().references(() => users.id, { onDelete: "cascade" }),
    // Foreign key to permissions table
    permissionId: bigint4("permission_id", { mode: "number" }).notNull().references(() => permissions.id, { onDelete: "cascade" }),
    // Grant or revoke
    // true: Grant this permission (even if role doesn't have it)
    // false: Revoke this permission (even if role has it)
    granted: boolean5("granted").notNull().default(true),
    // Reason for grant/revocation (audit trail)
    reason: text8("reason"),
    // Expiration timestamp (optional)
    // null: Permanent override
    // timestamp: Permission expires at this time
    expiresAt: timestamp6("expires_at", { withTimezone: true }),
    ...timestamps8()
  },
  (table) => [
    // Indexes for query performance
    index8("user_permissions_user_id_idx").on(table.userId),
    index8("user_permissions_permission_id_idx").on(table.permissionId),
    index8("user_permissions_expires_at_idx").on(table.expiresAt),
    // Unique constraint: one user-permission pair only
    unique2("user_permissions_unique").on(table.userId, table.permissionId)
  ]
);
export {
  authSchema,
  invitations,
  permissions,
  rolePermissions,
  roles,
  userPermissions,
  userPublicKeys,
  userSocialAccounts,
  users,
  verificationCodes
};
//# sourceMappingURL=index.js.map