|
|
@@ -0,0 +1,423 @@
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
|
|
|
mysqlTable, varchar, int, text, boolean, timestamp, json,
|
|
|
|
|
|
|
|
mysqlEnum, uniqueIndex, index, serial
|
|
|
|
|
|
|
|
} from 'drizzle-orm/mysql-core';
|
|
|
|
|
|
|
|
import { relations } from 'drizzle-orm';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Auth ──────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const users = mysqlTable('users', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
email: varchar({ length: 255 }).notNull().unique(),
|
|
|
|
|
|
|
|
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
role: mysqlEnum(['sysadmin', 'director', 'foster_coordinator', 'volunteer_manager', 'vet_liaison', 'content_editor', 'applications_manager', 'viewer', 'foster']).notNull().default('viewer'),
|
|
|
|
|
|
|
|
active: boolean().notNull().default(false),
|
|
|
|
|
|
|
|
mustChangePassword: boolean('must_change_password').notNull().default(false),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
|
|
|
|
|
|
|
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const sessions = mysqlTable('sessions', {
|
|
|
|
|
|
|
|
sessionId: varchar('session_id', { length: 255 }).primaryKey(),
|
|
|
|
|
|
|
|
userId: int('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
expiresAt: timestamp('expires_at').notNull()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Pets ──────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const intakeSources = mysqlTable('intake_sources', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
active: boolean().notNull().default(true)
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const vets = mysqlTable('vets', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
clinic: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
phone: varchar({ length: 50 }),
|
|
|
|
|
|
|
|
email: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
address: text(),
|
|
|
|
|
|
|
|
notes: text(),
|
|
|
|
|
|
|
|
active: boolean().notNull().default(true),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const pets = mysqlTable('pets', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
slug: varchar({ length: 255 }).notNull().unique(),
|
|
|
|
|
|
|
|
species: mysqlEnum(['dog', 'cat', 'other']).notNull().default('dog'),
|
|
|
|
|
|
|
|
breed: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
color: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
markings: text(),
|
|
|
|
|
|
|
|
sex: mysqlEnum(['male', 'female', 'unknown']).notNull().default('unknown'),
|
|
|
|
|
|
|
|
weight: varchar({ length: 50 }),
|
|
|
|
|
|
|
|
age: varchar({ length: 100 }),
|
|
|
|
|
|
|
|
birthday: timestamp(),
|
|
|
|
|
|
|
|
status: mysqlEnum([
|
|
|
|
|
|
|
|
'available', 'adopted', 'foster', 'medical_hold', 'behavioral_hold',
|
|
|
|
|
|
|
|
'pending', 'trial', 'hospice', 'nursing', 'whelping', 'deceased', 'transferred'
|
|
|
|
|
|
|
|
]).notNull().default('available'),
|
|
|
|
|
|
|
|
description: text(),
|
|
|
|
|
|
|
|
medicalNotes: text('medical_notes'),
|
|
|
|
|
|
|
|
behavioralNotes: text('behavioral_notes'),
|
|
|
|
|
|
|
|
specialNeeds: text('special_needs'),
|
|
|
|
|
|
|
|
goodWithDogs: boolean('good_with_dogs'),
|
|
|
|
|
|
|
|
goodWithCats: boolean('good_with_cats'),
|
|
|
|
|
|
|
|
goodWithKids: boolean('good_with_kids'),
|
|
|
|
|
|
|
|
houseTrained: boolean('house_trained'),
|
|
|
|
|
|
|
|
spayedNeutered: boolean('spayed_neutered'),
|
|
|
|
|
|
|
|
microchipped: boolean(),
|
|
|
|
|
|
|
|
microchipId: varchar('microchip_id', { length: 100 }),
|
|
|
|
|
|
|
|
adoptionFee: int('adoption_fee'),
|
|
|
|
|
|
|
|
adoptionFeeCollected: boolean('adoption_fee_collected').default(false),
|
|
|
|
|
|
|
|
intakeDate: timestamp('intake_date'),
|
|
|
|
|
|
|
|
intakeSourceId: int('intake_source_id').references(() => intakeSources.id),
|
|
|
|
|
|
|
|
adoptedDate: timestamp('adopted_date'),
|
|
|
|
|
|
|
|
adopterName: varchar('adopter_name', { length: 255 }),
|
|
|
|
|
|
|
|
adopterEmail: varchar('adopter_email', { length: 255 }),
|
|
|
|
|
|
|
|
adopterPhone: varchar('adopter_phone', { length: 50 }),
|
|
|
|
|
|
|
|
vetId: int('vet_id').references(() => vets.id),
|
|
|
|
|
|
|
|
featured: boolean().notNull().default(false),
|
|
|
|
|
|
|
|
published: boolean().notNull().default(true),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
|
|
|
|
|
|
|
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
|
|
|
|
|
|
|
|
}, (table) => [
|
|
|
|
|
|
|
|
index('status_idx').on(table.status),
|
|
|
|
|
|
|
|
index('species_idx').on(table.species),
|
|
|
|
|
|
|
|
index('featured_idx').on(table.featured)
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const petPhotos = mysqlTable('pet_photos', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
url: varchar({ length: 500 }).notNull(),
|
|
|
|
|
|
|
|
alt: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
isPrimary: boolean('is_primary').notNull().default(false),
|
|
|
|
|
|
|
|
sortOrder: int('sort_order').notNull().default(0),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const petMedical = mysqlTable('pet_medical', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
type: mysqlEnum([
|
|
|
|
|
|
|
|
'vaccine', 'surgery', 'checkup', 'medication', 'deworming',
|
|
|
|
|
|
|
|
'flea_tick', 'heartworm_test', 'heartworm_treatment',
|
|
|
|
|
|
|
|
'dental', 'spay_neuter', 'microchip', 'other'
|
|
|
|
|
|
|
|
]).notNull(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
date: timestamp(),
|
|
|
|
|
|
|
|
vetId: int('vet_id').references(() => vets.id),
|
|
|
|
|
|
|
|
cost: int(),
|
|
|
|
|
|
|
|
notes: text(),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const fosterAssignments = mysqlTable('foster_assignments', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
userId: int('user_id').notNull().references(() => users.id),
|
|
|
|
|
|
|
|
startDate: timestamp('start_date').notNull().defaultNow(),
|
|
|
|
|
|
|
|
endDate: timestamp('end_date'),
|
|
|
|
|
|
|
|
isCurrent: boolean('is_current').notNull().default(true),
|
|
|
|
|
|
|
|
notes: text(),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const mentors = mysqlTable('mentors', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
userId: int('user_id').notNull().references(() => users.id).unique(),
|
|
|
|
|
|
|
|
bio: text(),
|
|
|
|
|
|
|
|
active: boolean().notNull().default(true),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const petMentors = mysqlTable('pet_mentors', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
mentorId: int('mentor_id').notNull().references(() => mentors.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
assignedAt: timestamp('assigned_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Applications ──────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const applications = mysqlTable('applications', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
type: mysqlEnum(['adoption', 'foster', 'volunteer', 'surrender']).notNull(),
|
|
|
|
|
|
|
|
status: mysqlEnum(['pending', 'reviewing', 'approved', 'denied', 'withdrawn']).notNull().default('pending'),
|
|
|
|
|
|
|
|
applicantName: varchar('applicant_name', { length: 255 }).notNull(),
|
|
|
|
|
|
|
|
applicantEmail: varchar('applicant_email', { length: 255 }).notNull(),
|
|
|
|
|
|
|
|
applicantPhone: varchar('applicant_phone', { length: 50 }),
|
|
|
|
|
|
|
|
petId: int('pet_id').references(() => pets.id),
|
|
|
|
|
|
|
|
formData: json('form_data').notNull(),
|
|
|
|
|
|
|
|
reviewedBy: int('reviewed_by').references(() => users.id),
|
|
|
|
|
|
|
|
reviewedAt: timestamp('reviewed_at'),
|
|
|
|
|
|
|
|
reviewNotes: text('review_notes'),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
|
|
|
|
|
|
|
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const applicationHomevisitNotes = mysqlTable('application_homevisit_notes', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
applicationId: int('application_id').notNull().references(() => applications.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
userId: int('user_id').notNull().references(() => users.id),
|
|
|
|
|
|
|
|
notes: text().notNull(),
|
|
|
|
|
|
|
|
visitDate: timestamp('visit_date'),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Finance ───────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const sponsors = mysqlTable('sponsors', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
tier: mysqlEnum(['top_dog', 'wagging_tails', 'wet_noses']).notNull().default('wet_noses'),
|
|
|
|
|
|
|
|
logoUrl: varchar('logo_url', { length: 500 }),
|
|
|
|
|
|
|
|
websiteUrl: varchar('website_url', { length: 500 }),
|
|
|
|
|
|
|
|
description: text(),
|
|
|
|
|
|
|
|
active: boolean().notNull().default(true),
|
|
|
|
|
|
|
|
sortOrder: int('sort_order').notNull().default(0),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const donations = mysqlTable('donations', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
donorName: varchar('donor_name', { length: 255 }),
|
|
|
|
|
|
|
|
donorEmail: varchar('donor_email', { length: 255 }),
|
|
|
|
|
|
|
|
amount: int().notNull(),
|
|
|
|
|
|
|
|
source: mysqlEnum([
|
|
|
|
|
|
|
|
'stripe', 'paypal', 'venmo', 'givebutter', 'cash',
|
|
|
|
|
|
|
|
'check', 'zelle', 'google_pay', 'square', 'other'
|
|
|
|
|
|
|
|
]).notNull().default('other'),
|
|
|
|
|
|
|
|
petId: int('pet_id').references(() => pets.id),
|
|
|
|
|
|
|
|
stripePaymentId: varchar('stripe_payment_id', { length: 255 }),
|
|
|
|
|
|
|
|
isRecurring: boolean('is_recurring').notNull().default(false),
|
|
|
|
|
|
|
|
purpose: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
notes: text(),
|
|
|
|
|
|
|
|
date: timestamp().notNull().defaultNow(),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const expenses = mysqlTable('expenses', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
description: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
amount: int().notNull(),
|
|
|
|
|
|
|
|
category: varchar({ length: 100 }),
|
|
|
|
|
|
|
|
petId: int('pet_id').references(() => pets.id),
|
|
|
|
|
|
|
|
vetId: int('vet_id').references(() => vets.id),
|
|
|
|
|
|
|
|
receiptUrl: varchar('receipt_url', { length: 500 }),
|
|
|
|
|
|
|
|
date: timestamp().notNull().defaultNow(),
|
|
|
|
|
|
|
|
notes: text(),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Content ───────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const events = mysqlTable('events', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
title: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
description: text(),
|
|
|
|
|
|
|
|
location: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
startDate: timestamp('start_date').notNull(),
|
|
|
|
|
|
|
|
endDate: timestamp('end_date'),
|
|
|
|
|
|
|
|
imageUrl: varchar('image_url', { length: 500 }),
|
|
|
|
|
|
|
|
published: boolean().notNull().default(true),
|
|
|
|
|
|
|
|
facebookEventId: varchar('facebook_event_id', { length: 100 }),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const happyTails = mysqlTable('happy_tails', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
petId: int('pet_id').references(() => pets.id),
|
|
|
|
|
|
|
|
title: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
story: text().notNull(),
|
|
|
|
|
|
|
|
imageUrl: varchar('image_url', { length: 500 }),
|
|
|
|
|
|
|
|
adopterName: varchar('adopter_name', { length: 255 }),
|
|
|
|
|
|
|
|
featured: boolean().notNull().default(false),
|
|
|
|
|
|
|
|
published: boolean().notNull().default(false),
|
|
|
|
|
|
|
|
submittedBy: varchar('submitted_by', { length: 255 }),
|
|
|
|
|
|
|
|
submittedEmail: varchar('submitted_email', { length: 255 }),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const contentBlocks = mysqlTable('content_blocks', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
page: varchar({ length: 100 }).notNull(),
|
|
|
|
|
|
|
|
sectionKey: varchar('section_key', { length: 100 }).notNull(),
|
|
|
|
|
|
|
|
content: json().notNull(),
|
|
|
|
|
|
|
|
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
|
|
|
|
|
|
|
|
}, (table) => [
|
|
|
|
|
|
|
|
uniqueIndex('page_section_idx').on(table.page, table.sectionKey)
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const newsletterSubscribers = mysqlTable('newsletter_subscribers', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
email: varchar({ length: 255 }).notNull().unique(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
subscribedAt: timestamp('subscribed_at').notNull().defaultNow(),
|
|
|
|
|
|
|
|
unsubscribedAt: timestamp('unsubscribed_at')
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const contactMessages = mysqlTable('contact_messages', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
name: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
email: varchar({ length: 255 }).notNull(),
|
|
|
|
|
|
|
|
subject: varchar({ length: 255 }),
|
|
|
|
|
|
|
|
message: text().notNull(),
|
|
|
|
|
|
|
|
read: boolean().notNull().default(false),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Foster Portal ─────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const fosterSubmissions = mysqlTable('foster_submissions', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
userId: int('user_id').notNull().references(() => users.id),
|
|
|
|
|
|
|
|
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
|
|
|
|
|
|
|
|
type: mysqlEnum(['note', 'photo', 'update']).notNull().default('update'),
|
|
|
|
|
|
|
|
content: text(),
|
|
|
|
|
|
|
|
photoUrl: varchar('photo_url', { length: 500 }),
|
|
|
|
|
|
|
|
status: mysqlEnum(['pending', 'approved', 'rejected']).notNull().default('pending'),
|
|
|
|
|
|
|
|
reviewedBy: int('reviewed_by').references(() => users.id),
|
|
|
|
|
|
|
|
reviewedAt: timestamp('reviewed_at'),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const supplyRequests = mysqlTable('supply_requests', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
userId: int('user_id').notNull().references(() => users.id),
|
|
|
|
|
|
|
|
petId: int('pet_id').references(() => pets.id),
|
|
|
|
|
|
|
|
items: text().notNull(),
|
|
|
|
|
|
|
|
urgency: mysqlEnum(['low', 'medium', 'high']).notNull().default('medium'),
|
|
|
|
|
|
|
|
status: mysqlEnum(['pending', 'approved', 'fulfilled', 'denied']).notNull().default('pending'),
|
|
|
|
|
|
|
|
notes: text(),
|
|
|
|
|
|
|
|
reviewedBy: int('reviewed_by').references(() => users.id),
|
|
|
|
|
|
|
|
reviewedAt: timestamp('reviewed_at'),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Config & System ───────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const siteSettings = mysqlTable('site_settings', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
key: varchar({ length: 100 }).notNull().unique(),
|
|
|
|
|
|
|
|
value: text().notNull(),
|
|
|
|
|
|
|
|
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const orgConfig = mysqlTable('org_config', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
key: varchar({ length: 100 }).notNull().unique(),
|
|
|
|
|
|
|
|
value: json().notNull(),
|
|
|
|
|
|
|
|
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const auditLog = mysqlTable('audit_log', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
userId: int('user_id').references(() => users.id),
|
|
|
|
|
|
|
|
action: varchar({ length: 100 }).notNull(),
|
|
|
|
|
|
|
|
entity: varchar({ length: 100 }),
|
|
|
|
|
|
|
|
entityId: int('entity_id'),
|
|
|
|
|
|
|
|
diff: json(),
|
|
|
|
|
|
|
|
ipAddress: varchar('ip_address', { length: 45 }),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
}, (table) => [
|
|
|
|
|
|
|
|
index('action_idx').on(table.action),
|
|
|
|
|
|
|
|
index('entity_idx').on(table.entity, table.entityId)
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const pageViews = mysqlTable('page_views', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
path: varchar({ length: 500 }).notNull(),
|
|
|
|
|
|
|
|
petId: int('pet_id').references(() => pets.id),
|
|
|
|
|
|
|
|
ipAddress: varchar('ip_address', { length: 45 }),
|
|
|
|
|
|
|
|
userAgent: text('user_agent'),
|
|
|
|
|
|
|
|
referrer: varchar({ length: 500 }),
|
|
|
|
|
|
|
|
isBot: boolean('is_bot').notNull().default(false),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
}, (table) => [
|
|
|
|
|
|
|
|
index('path_idx').on(table.path),
|
|
|
|
|
|
|
|
index('created_idx').on(table.createdAt)
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const aiJobs = mysqlTable('ai_jobs', {
|
|
|
|
|
|
|
|
id: serial().primaryKey(),
|
|
|
|
|
|
|
|
type: varchar({ length: 50 }).notNull(),
|
|
|
|
|
|
|
|
input: json().notNull(),
|
|
|
|
|
|
|
|
status: mysqlEnum(['pending', 'processing', 'completed', 'failed']).notNull().default('pending'),
|
|
|
|
|
|
|
|
output: json(),
|
|
|
|
|
|
|
|
error: text(),
|
|
|
|
|
|
|
|
completedAt: timestamp('completed_at'),
|
|
|
|
|
|
|
|
createdAt: timestamp('created_at').notNull().defaultNow()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Relations ─────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const usersRelations = relations(users, ({ many }) => ({
|
|
|
|
|
|
|
|
sessions: many(sessions),
|
|
|
|
|
|
|
|
fosterAssignments: many(fosterAssignments),
|
|
|
|
|
|
|
|
fosterSubmissions: many(fosterSubmissions),
|
|
|
|
|
|
|
|
supplyRequests: many(supplyRequests)
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const sessionsRelations = relations(sessions, ({ one }) => ({
|
|
|
|
|
|
|
|
user: one(users, { fields: [sessions.userId], references: [users.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const petsRelations = relations(pets, ({ one, many }) => ({
|
|
|
|
|
|
|
|
photos: many(petPhotos),
|
|
|
|
|
|
|
|
medical: many(petMedical),
|
|
|
|
|
|
|
|
fosterAssignments: many(fosterAssignments),
|
|
|
|
|
|
|
|
applications: many(applications),
|
|
|
|
|
|
|
|
intakeSource: one(intakeSources, { fields: [pets.intakeSourceId], references: [intakeSources.id] }),
|
|
|
|
|
|
|
|
vet: one(vets, { fields: [pets.vetId], references: [vets.id] }),
|
|
|
|
|
|
|
|
donations: many(donations),
|
|
|
|
|
|
|
|
expenses: many(expenses)
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const petPhotosRelations = relations(petPhotos, ({ one }) => ({
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [petPhotos.petId], references: [pets.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const petMedicalRelations = relations(petMedical, ({ one }) => ({
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [petMedical.petId], references: [pets.id] }),
|
|
|
|
|
|
|
|
vet: one(vets, { fields: [petMedical.vetId], references: [vets.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const fosterAssignmentsRelations = relations(fosterAssignments, ({ one }) => ({
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [fosterAssignments.petId], references: [pets.id] }),
|
|
|
|
|
|
|
|
user: one(users, { fields: [fosterAssignments.userId], references: [users.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const applicationsRelations = relations(applications, ({ one, many }) => ({
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [applications.petId], references: [pets.id] }),
|
|
|
|
|
|
|
|
reviewer: one(users, { fields: [applications.reviewedBy], references: [users.id] }),
|
|
|
|
|
|
|
|
homevisitNotes: many(applicationHomevisitNotes)
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const applicationHomevisitNotesRelations = relations(applicationHomevisitNotes, ({ one }) => ({
|
|
|
|
|
|
|
|
application: one(applications, { fields: [applicationHomevisitNotes.applicationId], references: [applications.id] }),
|
|
|
|
|
|
|
|
user: one(users, { fields: [applicationHomevisitNotes.userId], references: [users.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const donationsRelations = relations(donations, ({ one }) => ({
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [donations.petId], references: [pets.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const expensesRelations = relations(expenses, ({ one }) => ({
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [expenses.petId], references: [pets.id] }),
|
|
|
|
|
|
|
|
vet: one(vets, { fields: [expenses.vetId], references: [vets.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const fosterSubmissionsRelations = relations(fosterSubmissions, ({ one }) => ({
|
|
|
|
|
|
|
|
user: one(users, { fields: [fosterSubmissions.userId], references: [users.id] }),
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [fosterSubmissions.petId], references: [pets.id] })
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const supplyRequestsRelations = relations(supplyRequests, ({ one }) => ({
|
|
|
|
|
|
|
|
user: one(users, { fields: [supplyRequests.userId], references: [users.id] }),
|
|
|
|
|
|
|
|
pet: one(pets, { fields: [supplyRequests.petId], references: [pets.id] })
|
|
|
|
|
|
|
|
}));
|