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:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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'),
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user