chore: commit all changes

This commit is contained in:
2026-02-26 13:26:18 -05:00
parent fd8edb1f21
commit 74fee52c4e
26 changed files with 4705 additions and 1108 deletions

View File

@@ -1,14 +1,14 @@
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { authSchema } from './schema';
import { schema } from './schema';
type AuthDrizzleDb = ReturnType<typeof createDb>;
type AppDrizzleDb = ReturnType<typeof createDb>;
declare global {
// eslint-disable-next-line no-var
var __fiscalAuthPgPool: Pool | undefined;
var __fiscalPgPool: Pool | undefined;
// eslint-disable-next-line no-var
var __fiscalAuthDrizzleDb: AuthDrizzleDb | undefined;
var __fiscalDrizzleDb: AppDrizzleDb | undefined;
}
function getConnectionString() {
@@ -21,21 +21,21 @@ function getConnectionString() {
}
export function getPool() {
if (!globalThis.__fiscalAuthPgPool) {
globalThis.__fiscalAuthPgPool = new Pool({
if (!globalThis.__fiscalPgPool) {
globalThis.__fiscalPgPool = new Pool({
connectionString: getConnectionString()
});
}
return globalThis.__fiscalAuthPgPool;
return globalThis.__fiscalPgPool;
}
function createDb() {
return drizzle(getPool(), { schema: authSchema });
return drizzle(getPool(), { schema });
}
export const db = globalThis.__fiscalAuthDrizzleDb ?? createDb();
export const db = globalThis.__fiscalDrizzleDb ?? createDb();
if (!globalThis.__fiscalAuthDrizzleDb) {
globalThis.__fiscalAuthDrizzleDb = db;
if (!globalThis.__fiscalDrizzleDb) {
globalThis.__fiscalDrizzleDb = db;
}

View File

@@ -1,22 +1,52 @@
import { boolean, index, pgTable, text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core';
import {
boolean,
index,
integer,
jsonb,
numeric,
pgTable,
text,
timestamp,
uniqueIndex
} from 'drizzle-orm/pg-core';
const dateColumn = {
type FilingMetrics = {
revenue: number | null;
netIncome: number | null;
totalAssets: number | null;
cash: number | null;
debt: number | null;
};
type FilingAnalysis = {
provider?: string;
model?: string;
text?: string;
legacyInsights?: string;
};
const authDateColumn = {
withTimezone: true,
mode: 'date'
} as const;
const appDateColumn = {
withTimezone: true,
mode: 'string'
} as const;
export const user = pgTable('user', {
id: text('id').primaryKey().notNull(),
name: text('name').notNull(),
email: text('email').notNull(),
emailVerified: boolean('emailVerified').notNull().default(false),
image: text('image'),
createdAt: timestamp('createdAt', dateColumn).notNull(),
updatedAt: timestamp('updatedAt', dateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull(),
role: text('role'),
banned: boolean('banned').default(false),
banReason: text('banReason'),
banExpires: timestamp('banExpires', dateColumn)
banExpires: timestamp('banExpires', authDateColumn)
}, (table) => ({
userEmailUnique: uniqueIndex('user_email_uidx').on(table.email)
}));
@@ -26,7 +56,7 @@ export const organization = pgTable('organization', {
name: text('name').notNull(),
slug: text('slug').notNull(),
logo: text('logo'),
createdAt: timestamp('createdAt', dateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
metadata: text('metadata')
}, (table) => ({
organizationSlugUnique: uniqueIndex('organization_slug_uidx').on(table.slug)
@@ -34,10 +64,10 @@ export const organization = pgTable('organization', {
export const session = pgTable('session', {
id: text('id').primaryKey().notNull(),
expiresAt: timestamp('expiresAt', dateColumn).notNull(),
expiresAt: timestamp('expiresAt', authDateColumn).notNull(),
token: text('token').notNull(),
createdAt: timestamp('createdAt', dateColumn).notNull(),
updatedAt: timestamp('updatedAt', dateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull(),
ipAddress: text('ipAddress'),
userAgent: text('userAgent'),
userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }),
@@ -56,12 +86,12 @@ export const account = pgTable('account', {
accessToken: text('accessToken'),
refreshToken: text('refreshToken'),
idToken: text('idToken'),
accessTokenExpiresAt: timestamp('accessTokenExpiresAt', dateColumn),
refreshTokenExpiresAt: timestamp('refreshTokenExpiresAt', dateColumn),
accessTokenExpiresAt: timestamp('accessTokenExpiresAt', authDateColumn),
refreshTokenExpiresAt: timestamp('refreshTokenExpiresAt', authDateColumn),
scope: text('scope'),
password: text('password'),
createdAt: timestamp('createdAt', dateColumn).notNull(),
updatedAt: timestamp('updatedAt', dateColumn).notNull()
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull()
}, (table) => ({
accountUserIdIndex: index('account_userId_idx').on(table.userId)
}));
@@ -70,9 +100,9 @@ export const verification = pgTable('verification', {
id: text('id').primaryKey().notNull(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expiresAt', dateColumn).notNull(),
createdAt: timestamp('createdAt', dateColumn).notNull(),
updatedAt: timestamp('updatedAt', dateColumn).notNull()
expiresAt: timestamp('expiresAt', authDateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull()
}, (table) => ({
verificationIdentifierIndex: index('verification_identifier_idx').on(table.identifier)
}));
@@ -82,7 +112,7 @@ export const member = pgTable('member', {
organizationId: text('organizationId').notNull().references(() => organization.id, { onDelete: 'cascade' }),
userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }),
role: text('role').notNull().default('member'),
createdAt: timestamp('createdAt', dateColumn).notNull()
createdAt: timestamp('createdAt', authDateColumn).notNull()
}, (table) => ({
memberOrganizationIdIndex: index('member_organizationId_idx').on(table.organizationId),
memberUserIdIndex: index('member_userId_idx').on(table.userId)
@@ -94,14 +124,109 @@ export const invitation = pgTable('invitation', {
email: text('email').notNull(),
role: text('role'),
status: text('status').notNull().default('pending'),
expiresAt: timestamp('expiresAt', dateColumn).notNull(),
createdAt: timestamp('createdAt', dateColumn).notNull(),
expiresAt: timestamp('expiresAt', authDateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
inviterId: text('inviterId').notNull().references(() => user.id, { onDelete: 'cascade' })
}, (table) => ({
invitationOrganizationIdIndex: index('invitation_organizationId_idx').on(table.organizationId),
invitationEmailIndex: index('invitation_email_idx').on(table.email)
}));
export const watchlistItem = pgTable('watchlist_item', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
ticker: text('ticker').notNull(),
company_name: text('company_name').notNull(),
sector: text('sector'),
created_at: timestamp('created_at', appDateColumn).notNull()
}, (table) => ({
watchlistUserTickerUnique: uniqueIndex('watchlist_user_ticker_uidx').on(table.user_id, table.ticker),
watchlistUserCreatedIndex: index('watchlist_user_created_idx').on(table.user_id, table.created_at)
}));
export const holding = pgTable('holding', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
ticker: text('ticker').notNull(),
shares: numeric('shares', { precision: 30, scale: 6 }).notNull(),
avg_cost: numeric('avg_cost', { precision: 30, scale: 6 }).notNull(),
current_price: numeric('current_price', { precision: 30, scale: 6 }),
market_value: numeric('market_value', { precision: 30, scale: 2 }).notNull(),
gain_loss: numeric('gain_loss', { precision: 30, scale: 2 }).notNull(),
gain_loss_pct: numeric('gain_loss_pct', { precision: 30, scale: 2 }).notNull(),
last_price_at: timestamp('last_price_at', appDateColumn),
created_at: timestamp('created_at', appDateColumn).notNull(),
updated_at: timestamp('updated_at', appDateColumn).notNull()
}, (table) => ({
holdingUserTickerUnique: uniqueIndex('holding_user_ticker_uidx').on(table.user_id, table.ticker),
holdingUserIndex: index('holding_user_idx').on(table.user_id)
}));
export const filing = pgTable('filing', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
ticker: text('ticker').notNull(),
filing_type: text('filing_type').$type<'10-K' | '10-Q' | '8-K'>().notNull(),
filing_date: text('filing_date').notNull(),
accession_number: text('accession_number').notNull(),
cik: text('cik').notNull(),
company_name: text('company_name').notNull(),
filing_url: text('filing_url'),
submission_url: text('submission_url'),
primary_document: text('primary_document'),
metrics: jsonb('metrics').$type<FilingMetrics | null>(),
analysis: jsonb('analysis').$type<FilingAnalysis | null>(),
created_at: timestamp('created_at', appDateColumn).notNull(),
updated_at: timestamp('updated_at', appDateColumn).notNull()
}, (table) => ({
filingAccessionUnique: uniqueIndex('filing_accession_uidx').on(table.accession_number),
filingTickerDateIndex: index('filing_ticker_date_idx').on(table.ticker, table.filing_date),
filingDateIndex: index('filing_date_idx').on(table.filing_date)
}));
export const filingLink = pgTable('filing_link', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
filing_id: integer('filing_id').notNull().references(() => filing.id, { onDelete: 'cascade' }),
link_type: text('link_type').notNull(),
url: text('url').notNull(),
source: text('source').notNull().default('sec'),
created_at: timestamp('created_at', appDateColumn).notNull()
}, (table) => ({
filingLinkUnique: uniqueIndex('filing_link_unique_uidx').on(table.filing_id, table.url),
filingLinkFilingIndex: index('filing_link_filing_idx').on(table.filing_id)
}));
export const taskRun = pgTable('task_run', {
id: text('id').primaryKey().notNull(),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
task_type: text('task_type').$type<'sync_filings' | 'refresh_prices' | 'analyze_filing' | 'portfolio_insights'>().notNull(),
status: text('status').$type<'queued' | 'running' | 'completed' | 'failed'>().notNull(),
priority: integer('priority').notNull(),
payload: jsonb('payload').$type<Record<string, unknown>>().notNull(),
result: jsonb('result').$type<Record<string, unknown> | null>(),
error: text('error'),
attempts: integer('attempts').notNull(),
max_attempts: integer('max_attempts').notNull(),
workflow_run_id: text('workflow_run_id'),
created_at: timestamp('created_at', appDateColumn).notNull(),
updated_at: timestamp('updated_at', appDateColumn).notNull(),
finished_at: timestamp('finished_at', appDateColumn)
}, (table) => ({
taskUserCreatedIndex: index('task_user_created_idx').on(table.user_id, table.created_at),
taskStatusIndex: index('task_status_idx').on(table.status),
taskWorkflowRunUnique: uniqueIndex('task_workflow_run_uidx').on(table.workflow_run_id)
}));
export const portfolioInsight = pgTable('portfolio_insight', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
provider: text('provider').notNull(),
model: text('model').notNull(),
content: text('content').notNull(),
created_at: timestamp('created_at', appDateColumn).notNull()
}, (table) => ({
insightUserCreatedIndex: index('insight_user_created_idx').on(table.user_id, table.created_at)
}));
export const authSchema = {
user,
session,
@@ -111,3 +236,17 @@ export const authSchema = {
member,
invitation
};
export const appSchema = {
watchlistItem,
holding,
filing,
filingLink,
taskRun,
portfolioInsight
};
export const schema = {
...authSchema,
...appSchema
};