fix: MariaDB compatibility, Docker build, and StatsCounter props

- Replace serial() with int().autoincrement() for MariaDB compatibility
- Expose DB port 3306 in docker-compose for local dev tooling
- Remove drizzle/ COPY from Dockerfile (using push, not migrations)
- Fix StatsCounter props mismatch on home page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 13:17:51 -05:00
co-authored by Claude Opus 4.6
parent d192062793
commit 3b6159e6d0
4 changed files with 35 additions and 35 deletions
-1
View File
@@ -10,7 +10,6 @@ WORKDIR /app
COPY --from=builder /app/build ./build COPY --from=builder /app/build ./build
COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./ COPY --from=builder /app/package.json ./
COPY --from=builder /app/drizzle ./drizzle
COPY --from=builder /app/scripts ./scripts COPY --from=builder /app/scripts ./scripts
ENV NODE_ENV=production ENV NODE_ENV=production
+2
View File
@@ -7,6 +7,8 @@ services:
MYSQL_DATABASE: fosterflow MYSQL_DATABASE: fosterflow
MYSQL_USER: fosterflow MYSQL_USER: fosterflow
MYSQL_PASSWORD: ${DB_PASSWORD:-fosterflow} MYSQL_PASSWORD: ${DB_PASSWORD:-fosterflow}
ports:
- "3306:3306"
volumes: volumes:
- db_data:/var/lib/mysql - db_data:/var/lib/mysql
healthcheck: healthcheck:
+27 -27
View File
@@ -1,13 +1,13 @@
import { import {
mysqlTable, varchar, int, text, boolean, timestamp, json, mysqlTable, varchar, int, text, boolean, timestamp, json,
mysqlEnum, uniqueIndex, index, serial mysqlEnum, uniqueIndex, index
} from 'drizzle-orm/mysql-core'; } from 'drizzle-orm/mysql-core';
import { relations } from 'drizzle-orm'; import { relations } from 'drizzle-orm';
// ── Auth ────────────────────────────────────────── // ── Auth ──────────────────────────────────────────
export const users = mysqlTable('users', { export const users = mysqlTable('users', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
email: varchar({ length: 255 }).notNull().unique(), email: varchar({ length: 255 }).notNull().unique(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(), passwordHash: varchar('password_hash', { length: 255 }).notNull(),
name: varchar({ length: 255 }).notNull(), name: varchar({ length: 255 }).notNull(),
@@ -27,13 +27,13 @@ export const sessions = mysqlTable('sessions', {
// ── Pets ────────────────────────────────────────── // ── Pets ──────────────────────────────────────────
export const intakeSources = mysqlTable('intake_sources', { export const intakeSources = mysqlTable('intake_sources', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
name: varchar({ length: 255 }).notNull(), name: varchar({ length: 255 }).notNull(),
active: boolean().notNull().default(true) active: boolean().notNull().default(true)
}); });
export const vets = mysqlTable('vets', { export const vets = mysqlTable('vets', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
name: varchar({ length: 255 }).notNull(), name: varchar({ length: 255 }).notNull(),
clinic: varchar({ length: 255 }), clinic: varchar({ length: 255 }),
phone: varchar({ length: 50 }), phone: varchar({ length: 50 }),
@@ -45,7 +45,7 @@ export const vets = mysqlTable('vets', {
}); });
export const pets = mysqlTable('pets', { export const pets = mysqlTable('pets', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
name: varchar({ length: 255 }).notNull(), name: varchar({ length: 255 }).notNull(),
slug: varchar({ length: 255 }).notNull().unique(), slug: varchar({ length: 255 }).notNull().unique(),
species: mysqlEnum(['dog', 'cat', 'other']).notNull().default('dog'), species: mysqlEnum(['dog', 'cat', 'other']).notNull().default('dog'),
@@ -91,7 +91,7 @@ export const pets = mysqlTable('pets', {
]); ]);
export const petPhotos = mysqlTable('pet_photos', { export const petPhotos = mysqlTable('pet_photos', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }), petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
url: varchar({ length: 500 }).notNull(), url: varchar({ length: 500 }).notNull(),
alt: varchar({ length: 255 }), alt: varchar({ length: 255 }),
@@ -101,7 +101,7 @@ export const petPhotos = mysqlTable('pet_photos', {
}); });
export const petMedical = mysqlTable('pet_medical', { export const petMedical = mysqlTable('pet_medical', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }), petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
type: mysqlEnum([ type: mysqlEnum([
'vaccine', 'surgery', 'checkup', 'medication', 'deworming', 'vaccine', 'surgery', 'checkup', 'medication', 'deworming',
@@ -117,7 +117,7 @@ export const petMedical = mysqlTable('pet_medical', {
}); });
export const fosterAssignments = mysqlTable('foster_assignments', { export const fosterAssignments = mysqlTable('foster_assignments', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }), petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
userId: int('user_id').notNull().references(() => users.id), userId: int('user_id').notNull().references(() => users.id),
startDate: timestamp('start_date').notNull().defaultNow(), startDate: timestamp('start_date').notNull().defaultNow(),
@@ -128,7 +128,7 @@ export const fosterAssignments = mysqlTable('foster_assignments', {
}); });
export const mentors = mysqlTable('mentors', { export const mentors = mysqlTable('mentors', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
userId: int('user_id').notNull().references(() => users.id).unique(), userId: int('user_id').notNull().references(() => users.id).unique(),
bio: text(), bio: text(),
active: boolean().notNull().default(true), active: boolean().notNull().default(true),
@@ -136,7 +136,7 @@ export const mentors = mysqlTable('mentors', {
}); });
export const petMentors = mysqlTable('pet_mentors', { export const petMentors = mysqlTable('pet_mentors', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }), petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
mentorId: int('mentor_id').notNull().references(() => mentors.id, { onDelete: 'cascade' }), mentorId: int('mentor_id').notNull().references(() => mentors.id, { onDelete: 'cascade' }),
assignedAt: timestamp('assigned_at').notNull().defaultNow() assignedAt: timestamp('assigned_at').notNull().defaultNow()
@@ -145,7 +145,7 @@ export const petMentors = mysqlTable('pet_mentors', {
// ── Applications ────────────────────────────────── // ── Applications ──────────────────────────────────
export const applications = mysqlTable('applications', { export const applications = mysqlTable('applications', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
type: mysqlEnum(['adoption', 'foster', 'volunteer', 'surrender']).notNull(), type: mysqlEnum(['adoption', 'foster', 'volunteer', 'surrender']).notNull(),
status: mysqlEnum(['pending', 'reviewing', 'approved', 'denied', 'withdrawn']).notNull().default('pending'), status: mysqlEnum(['pending', 'reviewing', 'approved', 'denied', 'withdrawn']).notNull().default('pending'),
applicantName: varchar('applicant_name', { length: 255 }).notNull(), applicantName: varchar('applicant_name', { length: 255 }).notNull(),
@@ -161,7 +161,7 @@ export const applications = mysqlTable('applications', {
}); });
export const applicationHomevisitNotes = mysqlTable('application_homevisit_notes', { export const applicationHomevisitNotes = mysqlTable('application_homevisit_notes', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
applicationId: int('application_id').notNull().references(() => applications.id, { onDelete: 'cascade' }), applicationId: int('application_id').notNull().references(() => applications.id, { onDelete: 'cascade' }),
userId: int('user_id').notNull().references(() => users.id), userId: int('user_id').notNull().references(() => users.id),
notes: text().notNull(), notes: text().notNull(),
@@ -172,7 +172,7 @@ export const applicationHomevisitNotes = mysqlTable('application_homevisit_notes
// ── Finance ─────────────────────────────────────── // ── Finance ───────────────────────────────────────
export const sponsors = mysqlTable('sponsors', { export const sponsors = mysqlTable('sponsors', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
name: varchar({ length: 255 }).notNull(), name: varchar({ length: 255 }).notNull(),
tier: mysqlEnum(['top_dog', 'wagging_tails', 'wet_noses']).notNull().default('wet_noses'), tier: mysqlEnum(['top_dog', 'wagging_tails', 'wet_noses']).notNull().default('wet_noses'),
logoUrl: varchar('logo_url', { length: 500 }), logoUrl: varchar('logo_url', { length: 500 }),
@@ -184,7 +184,7 @@ export const sponsors = mysqlTable('sponsors', {
}); });
export const donations = mysqlTable('donations', { export const donations = mysqlTable('donations', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
donorName: varchar('donor_name', { length: 255 }), donorName: varchar('donor_name', { length: 255 }),
donorEmail: varchar('donor_email', { length: 255 }), donorEmail: varchar('donor_email', { length: 255 }),
amount: int().notNull(), amount: int().notNull(),
@@ -202,7 +202,7 @@ export const donations = mysqlTable('donations', {
}); });
export const expenses = mysqlTable('expenses', { export const expenses = mysqlTable('expenses', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
description: varchar({ length: 255 }).notNull(), description: varchar({ length: 255 }).notNull(),
amount: int().notNull(), amount: int().notNull(),
category: varchar({ length: 100 }), category: varchar({ length: 100 }),
@@ -217,7 +217,7 @@ export const expenses = mysqlTable('expenses', {
// ── Content ─────────────────────────────────────── // ── Content ───────────────────────────────────────
export const events = mysqlTable('events', { export const events = mysqlTable('events', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
title: varchar({ length: 255 }).notNull(), title: varchar({ length: 255 }).notNull(),
description: text(), description: text(),
location: varchar({ length: 255 }), location: varchar({ length: 255 }),
@@ -230,7 +230,7 @@ export const events = mysqlTable('events', {
}); });
export const happyTails = mysqlTable('happy_tails', { export const happyTails = mysqlTable('happy_tails', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
petId: int('pet_id').references(() => pets.id), petId: int('pet_id').references(() => pets.id),
title: varchar({ length: 255 }).notNull(), title: varchar({ length: 255 }).notNull(),
story: text().notNull(), story: text().notNull(),
@@ -244,7 +244,7 @@ export const happyTails = mysqlTable('happy_tails', {
}); });
export const contentBlocks = mysqlTable('content_blocks', { export const contentBlocks = mysqlTable('content_blocks', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
page: varchar({ length: 100 }).notNull(), page: varchar({ length: 100 }).notNull(),
sectionKey: varchar('section_key', { length: 100 }).notNull(), sectionKey: varchar('section_key', { length: 100 }).notNull(),
content: json().notNull(), content: json().notNull(),
@@ -254,7 +254,7 @@ export const contentBlocks = mysqlTable('content_blocks', {
]); ]);
export const newsletterSubscribers = mysqlTable('newsletter_subscribers', { export const newsletterSubscribers = mysqlTable('newsletter_subscribers', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
email: varchar({ length: 255 }).notNull().unique(), email: varchar({ length: 255 }).notNull().unique(),
name: varchar({ length: 255 }), name: varchar({ length: 255 }),
subscribedAt: timestamp('subscribed_at').notNull().defaultNow(), subscribedAt: timestamp('subscribed_at').notNull().defaultNow(),
@@ -262,7 +262,7 @@ export const newsletterSubscribers = mysqlTable('newsletter_subscribers', {
}); });
export const contactMessages = mysqlTable('contact_messages', { export const contactMessages = mysqlTable('contact_messages', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
name: varchar({ length: 255 }).notNull(), name: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull(), email: varchar({ length: 255 }).notNull(),
subject: varchar({ length: 255 }), subject: varchar({ length: 255 }),
@@ -274,7 +274,7 @@ export const contactMessages = mysqlTable('contact_messages', {
// ── Foster Portal ───────────────────────────────── // ── Foster Portal ─────────────────────────────────
export const fosterSubmissions = mysqlTable('foster_submissions', { export const fosterSubmissions = mysqlTable('foster_submissions', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
userId: int('user_id').notNull().references(() => users.id), userId: int('user_id').notNull().references(() => users.id),
petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }), petId: int('pet_id').notNull().references(() => pets.id, { onDelete: 'cascade' }),
type: mysqlEnum(['note', 'photo', 'update']).notNull().default('update'), type: mysqlEnum(['note', 'photo', 'update']).notNull().default('update'),
@@ -287,7 +287,7 @@ export const fosterSubmissions = mysqlTable('foster_submissions', {
}); });
export const supplyRequests = mysqlTable('supply_requests', { export const supplyRequests = mysqlTable('supply_requests', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
userId: int('user_id').notNull().references(() => users.id), userId: int('user_id').notNull().references(() => users.id),
petId: int('pet_id').references(() => pets.id), petId: int('pet_id').references(() => pets.id),
items: text().notNull(), items: text().notNull(),
@@ -302,21 +302,21 @@ export const supplyRequests = mysqlTable('supply_requests', {
// ── Config & System ─────────────────────────────── // ── Config & System ───────────────────────────────
export const siteSettings = mysqlTable('site_settings', { export const siteSettings = mysqlTable('site_settings', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
key: varchar({ length: 100 }).notNull().unique(), key: varchar({ length: 100 }).notNull().unique(),
value: text().notNull(), value: text().notNull(),
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow() updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
}); });
export const orgConfig = mysqlTable('org_config', { export const orgConfig = mysqlTable('org_config', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
key: varchar({ length: 100 }).notNull().unique(), key: varchar({ length: 100 }).notNull().unique(),
value: json().notNull(), value: json().notNull(),
updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow() updatedAt: timestamp('updated_at').notNull().defaultNow().onUpdateNow()
}); });
export const auditLog = mysqlTable('audit_log', { export const auditLog = mysqlTable('audit_log', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
userId: int('user_id').references(() => users.id), userId: int('user_id').references(() => users.id),
action: varchar({ length: 100 }).notNull(), action: varchar({ length: 100 }).notNull(),
entity: varchar({ length: 100 }), entity: varchar({ length: 100 }),
@@ -330,7 +330,7 @@ export const auditLog = mysqlTable('audit_log', {
]); ]);
export const pageViews = mysqlTable('page_views', { export const pageViews = mysqlTable('page_views', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
path: varchar({ length: 500 }).notNull(), path: varchar({ length: 500 }).notNull(),
petId: int('pet_id').references(() => pets.id), petId: int('pet_id').references(() => pets.id),
ipAddress: varchar('ip_address', { length: 45 }), ipAddress: varchar('ip_address', { length: 45 }),
@@ -344,7 +344,7 @@ export const pageViews = mysqlTable('page_views', {
]); ]);
export const aiJobs = mysqlTable('ai_jobs', { export const aiJobs = mysqlTable('ai_jobs', {
id: serial().primaryKey(), id: int().autoincrement().primaryKey(),
type: varchar({ length: 50 }).notNull(), type: varchar({ length: 50 }).notNull(),
input: json().notNull(), input: json().notNull(),
status: mysqlEnum(['pending', 'processing', 'completed', 'failed']).notNull().default('pending'), status: mysqlEnum(['pending', 'processing', 'completed', 'failed']).notNull().default('pending'),
+6 -7
View File
@@ -64,13 +64,12 @@
</section> </section>
<!-- Stats Counter --> <!-- Stats Counter -->
<StatsCounter <StatsCounter stats={[
totalRescued={data.stats.totalRescued} { label: 'Pets Rescued', value: data.stats.totalRescued },
totalAdopted={data.stats.totalAdopted} { label: 'Pets Adopted', value: data.stats.totalAdopted },
currentFosters={data.stats.currentFosters} { label: 'Available Now', value: data.stats.availableNow },
availableNow={data.stats.availableNow} { label: 'Active Fosters', value: data.stats.currentFosters }
yearsRescuing={data.stats.yearsRescuing} ]} />
/>
<!-- Featured Pets Section --> <!-- Featured Pets Section -->
<section class="py-16 md:py-24"> <section class="py-16 md:py-24">