Add database schema and Kysely packages

- Create initial database migration with full auth schema:
  - users, sessions, passkeys, devices tables
  - orgs, org_members, org_sites, org_invites tables
  - email_verifications, password_resets, login_requests tables
  - Indexes for common lookups and cleanup jobs
- Add @reviq/db-schema package with kysely-codegen
- Add @reviq/db package with Kysely client

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 11:44:36 +08:00
parent 322155b4a1
commit 392d976812
12 changed files with 1676 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import { configs } from "@macalinao/eslint-config";
export default [
...configs.fast,
{
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
},
},
},
];

View File

@@ -0,0 +1,26 @@
{
"name": "@reviq/db-schema",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsc",
"clean": "tsc --build --clean && rm -rf dist/ node_modules/ .eslintcache",
"lint": "eslint . --cache",
"generate": "kysely-codegen --dialect postgres --url $DATABASE_URL --out-file src/types.ts"
},
"dependencies": {
"kysely": "^0.28.9",
"pg": "^8.13.1"
},
"devDependencies": {
"kysely-codegen": "^0.19.0",
"@types/node": "^25.0.3",
"@types/pg": "^8.11.10",
"@macalinao/tsconfig": "catalog:",
"typescript": "catalog:"
}
}

View File

@@ -0,0 +1,10 @@
/**
* Database schema types generated from PostgreSQL schema
*
* @module @reviq/db-schema
*/
export * from "./types.js";
// Re-export DB as Database for convenience
export type { DB as Database } from "./types.js";

View File

@@ -0,0 +1,199 @@
/**
* This file was generated by kysely-codegen.
* Please do not edit it manually.
*/
import type { ColumnType } from "kysely";
export type Generated<T> =
T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>;
export type Int8 = ColumnType<string, bigint | number | string>;
export type Json = JsonValue;
export type JsonArray = JsonValue[];
export type JsonObject = {
[x: string]: JsonValue | undefined;
};
export type JsonPrimitive = boolean | number | string | null;
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
export type OrgRole = "admin" | "member" | "owner";
export type PasskeyDeviceType = "multiDevice" | "singleDevice";
export type Timestamp = ColumnType<Date, Date | string>;
export interface ApiTokens {
created_at: Generated<Timestamp>;
expires_at: Timestamp;
id: Generated<Int8>;
last_used_at: Timestamp | null;
name: string;
token_hash: string;
user_id: number;
}
export interface EmailVerifications {
created_at: Generated<Timestamp>;
expires_at: Timestamp;
id: Generated<Int8>;
token: string;
user_id: number;
}
export interface LoginRequests {
city: string | null;
completed_at: Timestamp | null;
country: string | null;
created_at: Generated<Timestamp>;
device_fingerprint: string | null;
email: string;
expires_at: Timestamp;
id: Generated<Int8>;
ip_address: string | null;
region: string | null;
token: string | null;
user_agent: string | null;
user_id: number;
}
export interface OrgInvites {
created_at: Generated<Timestamp>;
email: string;
expires_at: Timestamp;
id: Generated<number>;
invited_by: number;
org_id: number;
role: Generated<OrgRole>;
token: string;
}
export interface OrgMembers {
created_at: Generated<Timestamp>;
id: Generated<number>;
org_id: number;
role: Generated<OrgRole>;
user_id: number;
}
export interface Orgs {
created_at: Generated<Timestamp>;
display_name: string;
id: Generated<number>;
logo_url: string | null;
slug: string;
updated_at: Generated<Timestamp>;
}
export interface OrgSites {
created_at: Generated<Timestamp>;
domain: string;
id: Generated<number>;
org_id: number;
}
export interface Passkeys {
backup_eligible: boolean;
backup_status: boolean;
counter: Generated<Int8>;
created_at: Generated<Timestamp>;
credential_id: Buffer;
device_type: PasskeyDeviceType;
id: Generated<Int8>;
last_used_at: Timestamp | null;
name: string;
public_key: Buffer;
rpid: string;
transports: Json | null;
user_id: number;
webauthn_user_id: string;
}
export interface PasswordResets {
created_at: Generated<Timestamp>;
expires_at: Timestamp;
id: Generated<Int8>;
token: string;
used_at: Timestamp | null;
user_id: number;
}
export interface SchemaMigrations {
version: string;
}
export interface Sessions {
city: string | null;
country: string | null;
created_at: Generated<Timestamp>;
device_id: Int8 | null;
expires_at: Timestamp;
id: Generated<Int8>;
ip_address: string | null;
region: string | null;
revoked_at: Timestamp | null;
token_hash: string;
trusted_mode: boolean;
user_agent: string | null;
user_id: number;
}
export interface UserDevices {
city: string | null;
country: string | null;
created_at: Generated<Timestamp>;
device_fingerprint: string;
id: Generated<Int8>;
ip_address: string | null;
is_trusted: Generated<boolean>;
last_used_at: Generated<Timestamp>;
name: string | null;
region: string | null;
user_agent: string;
user_id: number;
}
export interface Users {
avatar_url: string | null;
created_at: Generated<Timestamp>;
display_name: string | null;
email: string;
email_verified_at: Timestamp | null;
full_name: string | null;
id: Generated<number>;
is_superuser: Generated<boolean>;
password_hash: string | null;
phone_number: string | null;
require_passkey: Generated<boolean>;
updated_at: Generated<Timestamp>;
}
export interface WebauthnChallenges {
created_at: Generated<Timestamp>;
id: Generated<Int8>;
options: Json;
}
export interface DB {
api_tokens: ApiTokens;
email_verifications: EmailVerifications;
login_requests: LoginRequests;
org_invites: OrgInvites;
org_members: OrgMembers;
org_sites: OrgSites;
orgs: Orgs;
passkeys: Passkeys;
password_resets: PasswordResets;
schema_migrations: SchemaMigrations;
sessions: Sessions;
user_devices: UserDevices;
users: Users;
webauthn_challenges: WebauthnChallenges;
}

View File

@@ -0,0 +1,15 @@
{
"extends": "@macalinao/tsconfig/tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"declarationMap": true,
"composite": true,
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}