146 lines
4.8 KiB
TypeScript
146 lines
4.8 KiB
TypeScript
import { postgresAdapter } from "@payloadcms/db-postgres";
|
|
import { lexicalEditor } from "@payloadcms/richtext-lexical";
|
|
import path from "path";
|
|
import { buildConfig } from "payload";
|
|
import { fileURLToPath } from "url";
|
|
import { s3Storage } from "@payloadcms/storage-s3";
|
|
|
|
import { Tenants } from "./collections/Tenants";
|
|
import Users from "./collections/Users";
|
|
import { Residents } from "./collections/Residents";
|
|
import { MealOrders } from "./collections/MealOrders";
|
|
import { Meals } from "./collections/Meals";
|
|
import { Media } from "./collections/Media";
|
|
import { multiTenantPlugin } from "@payloadcms/plugin-multi-tenant";
|
|
import { isSuperAdmin } from "./access/isSuperAdmin";
|
|
import type { Config } from "./payload-types";
|
|
import { getUserTenantIDs } from "./utilities/getUserTenantIDs";
|
|
import { seed } from "./seed";
|
|
|
|
const filename = fileURLToPath(import.meta.url);
|
|
const dirname = path.dirname(filename);
|
|
|
|
// Use PostgreSQL when DATABASE_URI is set, SQLite only for local development
|
|
// Migration commands:
|
|
// pnpm payload migrate:create - Create migration file
|
|
// pnpm payload migrate - Run pending migrations
|
|
// pnpm payload migrate:fresh - Drop all & re-run migrations
|
|
// pnpm payload migrate:reset - Rollback all migrations
|
|
// pnpm payload migrate:status - Check migration status
|
|
const getDatabaseAdapter = async () => {
|
|
if (process.env.DATABASE_URI) {
|
|
// Conditionally import migrations only when using PostgreSQL
|
|
const { migrations } = await import("./migrations");
|
|
return postgresAdapter({
|
|
pool: {
|
|
connectionString: process.env.DATABASE_URI,
|
|
},
|
|
// Use migration files from src/migrations/ instead of auto-push
|
|
push: false,
|
|
migrationDir: path.resolve(dirname, "migrations"),
|
|
prodMigrations: migrations,
|
|
});
|
|
}
|
|
|
|
// Only load SQLite in development (no DATABASE_URI set)
|
|
// Dynamic import to avoid loading libsql in production/Docker
|
|
const { sqliteAdapter } = await import("@payloadcms/db-sqlite");
|
|
return sqliteAdapter({
|
|
client: {
|
|
url: "file:./meal-planner.db",
|
|
},
|
|
// Use push mode for SQLite in development (auto-sync schema)
|
|
push: true,
|
|
});
|
|
};
|
|
// eslint-disable-next-line no-restricted-exports
|
|
export default buildConfig({
|
|
admin: {
|
|
user: "users",
|
|
meta: {
|
|
titleSuffix: "- Meal Planner",
|
|
},
|
|
components: {
|
|
views: {
|
|
kitchenDashboard: {
|
|
Component:
|
|
"@/app/(payload)/admin/views/KitchenDashboard#KitchenDashboard",
|
|
path: "/kitchen-dashboard",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
collections: [Users, Tenants, Residents, MealOrders, Meals, Media],
|
|
db: await getDatabaseAdapter(),
|
|
onInit: async (payload) => {
|
|
// Run migrations automatically on startup (for Docker/production)
|
|
payload.logger.info("Running database migrations...");
|
|
try {
|
|
await payload.db.migrate();
|
|
payload.logger.info("Migrations completed successfully");
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
payload.logger.error(`Migration failed: ${message}`);
|
|
// Don't throw - migrations may already be applied
|
|
}
|
|
|
|
// Seed database if SEED_DB is set
|
|
if (process.env.SEED_DB) {
|
|
await seed(payload);
|
|
}
|
|
},
|
|
editor: lexicalEditor({}),
|
|
graphQL: {
|
|
schemaOutputFile: path.resolve(dirname, "generated-schema.graphql"),
|
|
},
|
|
secret: process.env.PAYLOAD_SECRET as string,
|
|
typescript: {
|
|
outputFile: path.resolve(dirname, "payload-types.ts"),
|
|
},
|
|
plugins: [
|
|
// Conditionally add S3 storage when MINIO_ENDPOINT is set (Docker environment)
|
|
...(process.env.MINIO_ENDPOINT
|
|
? [
|
|
s3Storage({
|
|
collections: {
|
|
media: true,
|
|
},
|
|
bucket: process.env.S3_BUCKET || "meal-planner",
|
|
config: {
|
|
endpoint: process.env.MINIO_ENDPOINT,
|
|
region: process.env.S3_REGION || "us-east-1",
|
|
credentials: {
|
|
accessKeyId: process.env.S3_ACCESS_KEY_ID || "",
|
|
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY || "",
|
|
},
|
|
forcePathStyle: true, // Required for MinIO
|
|
},
|
|
}),
|
|
]
|
|
: []),
|
|
multiTenantPlugin<Config>({
|
|
collections: {
|
|
// Enable multi-tenancy for residents, meal orders, and meals
|
|
residents: {},
|
|
"meal-orders": {},
|
|
meals: {},
|
|
},
|
|
tenantField: {
|
|
access: {
|
|
read: () => true,
|
|
update: ({ req }) => {
|
|
if (isSuperAdmin(req.user)) {
|
|
return true;
|
|
}
|
|
return getUserTenantIDs(req.user).length > 0;
|
|
},
|
|
},
|
|
},
|
|
tenantsArrayField: {
|
|
includeDefaultField: false,
|
|
},
|
|
userHasAccessToAllTenants: (user) => isSuperAdmin(user),
|
|
}),
|
|
],
|
|
});
|