Refactor Docker entrypoint to run Prisma migrations; update schema
Co-authored-by: dennis <dennis@konkol.net>
This commit is contained in:
@@ -66,6 +66,7 @@ RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Copy the built application
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/scripts ./scripts
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
@@ -82,6 +83,8 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
# Copy Prisma files
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
||||
COPY --from=builder /app/node_modules/prisma ./node_modules/prisma
|
||||
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
|
||||
|
||||
# Note: Environment variables should be passed via docker-compose or runtime environment
|
||||
# DO NOT copy .env files into the image for security reasons
|
||||
@@ -97,4 +100,4 @@ ENV HOSTNAME="0.0.0.0"
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:3000/api/health || exit 1
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
CMD ["node", "scripts/start-with-migrate.js"]
|
||||
246
prisma/migrations/20260112150721_init/migration.sql
Normal file
246
prisma/migrations/20260112150721_init/migration.sql
Normal file
@@ -0,0 +1,246 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ContentStatus" AS ENUM ('DRAFT', 'PUBLISHED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Difficulty" AS ENUM ('BEGINNER', 'INTERMEDIATE', 'ADVANCED', 'EXPERT');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InteractionType" AS ENUM ('LIKE', 'SHARE', 'BOOKMARK', 'COMMENT');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Project" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"slug" VARCHAR(255) NOT NULL,
|
||||
"defaultLocale" VARCHAR(10) NOT NULL DEFAULT 'en',
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"tags" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"featured" BOOLEAN NOT NULL DEFAULT false,
|
||||
"category" VARCHAR(100) NOT NULL,
|
||||
"date" VARCHAR(10) NOT NULL,
|
||||
"github" VARCHAR(500),
|
||||
"live" VARCHAR(500),
|
||||
"published" BOOLEAN NOT NULL DEFAULT true,
|
||||
"imageUrl" VARCHAR(500),
|
||||
"metaDescription" TEXT,
|
||||
"keywords" TEXT,
|
||||
"ogImage" VARCHAR(500),
|
||||
"schema" JSONB,
|
||||
"difficulty" "Difficulty" NOT NULL DEFAULT 'INTERMEDIATE',
|
||||
"timeToComplete" VARCHAR(100),
|
||||
"technologies" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"challenges" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"lessonsLearned" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"futureImprovements" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"demoVideo" VARCHAR(500),
|
||||
"screenshots" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"colorScheme" VARCHAR(100) NOT NULL DEFAULT 'Dark',
|
||||
"accessibility" BOOLEAN NOT NULL DEFAULT true,
|
||||
"performance" JSONB NOT NULL DEFAULT '{"loadTime": "1.5s", "bundleSize": "50KB", "lighthouse": 90}',
|
||||
"analytics" JSONB NOT NULL DEFAULT '{"likes": 0, "views": 0, "shares": 0}',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Project_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "project_translations" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"project_id" INTEGER NOT NULL,
|
||||
"locale" VARCHAR(10) NOT NULL,
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"content" JSONB,
|
||||
"metaDescription" TEXT,
|
||||
"keywords" TEXT,
|
||||
"ogImage" VARCHAR(500),
|
||||
"schema" JSONB,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "project_translations_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "content_pages" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"key" VARCHAR(100) NOT NULL,
|
||||
"status" "ContentStatus" NOT NULL DEFAULT 'PUBLISHED',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "content_pages_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "content_page_translations" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"page_id" INTEGER NOT NULL,
|
||||
"locale" VARCHAR(10) NOT NULL,
|
||||
"title" TEXT,
|
||||
"slug" VARCHAR(255),
|
||||
"content" JSONB NOT NULL,
|
||||
"metaDescription" TEXT,
|
||||
"keywords" TEXT,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "content_page_translations_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "site_settings" (
|
||||
"id" INTEGER NOT NULL DEFAULT 1,
|
||||
"defaultLocale" VARCHAR(10) NOT NULL DEFAULT 'en',
|
||||
"locales" TEXT[] DEFAULT ARRAY['en', 'de']::TEXT[],
|
||||
"theme" JSONB,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "site_settings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PageView" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"project_id" INTEGER,
|
||||
"page" VARCHAR(100) NOT NULL,
|
||||
"ip" VARCHAR(45),
|
||||
"user_agent" TEXT,
|
||||
"referrer" VARCHAR(500),
|
||||
"timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "PageView_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "UserInteraction" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"project_id" INTEGER NOT NULL,
|
||||
"type" "InteractionType" NOT NULL,
|
||||
"ip" VARCHAR(45),
|
||||
"user_agent" TEXT,
|
||||
"timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "UserInteraction_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Contact" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" VARCHAR(255) NOT NULL,
|
||||
"email" VARCHAR(255) NOT NULL,
|
||||
"subject" VARCHAR(500) NOT NULL,
|
||||
"message" TEXT NOT NULL,
|
||||
"responded" BOOLEAN NOT NULL DEFAULT false,
|
||||
"response_template" VARCHAR(50),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Contact_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "activity_status" (
|
||||
"id" INTEGER NOT NULL DEFAULT 1,
|
||||
"activity_type" VARCHAR(50),
|
||||
"activity_details" VARCHAR(255),
|
||||
"activity_project" VARCHAR(255),
|
||||
"activity_language" VARCHAR(50),
|
||||
"activity_repo" VARCHAR(500),
|
||||
"music_playing" BOOLEAN NOT NULL DEFAULT false,
|
||||
"music_track" VARCHAR(255),
|
||||
"music_artist" VARCHAR(255),
|
||||
"music_album" VARCHAR(255),
|
||||
"music_platform" VARCHAR(50),
|
||||
"music_progress" INTEGER,
|
||||
"music_album_art" VARCHAR(500),
|
||||
"watching_title" VARCHAR(255),
|
||||
"watching_platform" VARCHAR(50),
|
||||
"watching_type" VARCHAR(50),
|
||||
"gaming_game" VARCHAR(255),
|
||||
"gaming_platform" VARCHAR(50),
|
||||
"gaming_status" VARCHAR(50),
|
||||
"status_mood" VARCHAR(50),
|
||||
"status_message" VARCHAR(500),
|
||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "activity_status_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Project_slug_key" ON "Project"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Project_category_idx" ON "Project"("category");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Project_featured_idx" ON "Project"("featured");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Project_published_idx" ON "Project"("published");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Project_difficulty_idx" ON "Project"("difficulty");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Project_created_at_idx" ON "Project"("created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Project_tags_idx" ON "Project"("tags");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "project_translations_locale_idx" ON "project_translations"("locale");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "project_translations_project_id_idx" ON "project_translations"("project_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "project_translations_project_id_locale_key" ON "project_translations"("project_id", "locale");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "content_pages_key_key" ON "content_pages"("key");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "content_page_translations_locale_idx" ON "content_page_translations"("locale");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "content_page_translations_slug_idx" ON "content_page_translations"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "content_page_translations_page_id_locale_key" ON "content_page_translations"("page_id", "locale");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PageView_project_id_idx" ON "PageView"("project_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PageView_timestamp_idx" ON "PageView"("timestamp");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PageView_page_idx" ON "PageView"("page");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UserInteraction_project_id_idx" ON "UserInteraction"("project_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UserInteraction_type_idx" ON "UserInteraction"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UserInteraction_timestamp_idx" ON "UserInteraction"("timestamp");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Contact_email_idx" ON "Contact"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Contact_responded_idx" ON "Contact"("responded");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Contact_created_at_idx" ON "Contact"("created_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "project_translations" ADD CONSTRAINT "project_translations_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "content_page_translations" ADD CONSTRAINT "content_page_translations_page_id_fkey" FOREIGN KEY ("page_id") REFERENCES "content_pages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
# Database Migrations
|
||||
|
||||
This directory contains SQL migration scripts for manual database updates.
|
||||
|
||||
## Running Migrations
|
||||
|
||||
### Method 1: Using psql (Recommended)
|
||||
|
||||
```bash
|
||||
# Connect to your database
|
||||
psql -d portfolio -f prisma/migrations/create_activity_status.sql
|
||||
|
||||
# Or with connection string
|
||||
psql "postgresql://user:password@localhost:5432/portfolio" -f prisma/migrations/create_activity_status.sql
|
||||
```
|
||||
|
||||
### Method 2: Using Docker
|
||||
|
||||
```bash
|
||||
# If your database is in Docker
|
||||
docker exec -i postgres_container psql -U username -d portfolio < prisma/migrations/create_activity_status.sql
|
||||
```
|
||||
|
||||
### Method 3: Using pgAdmin or Database GUI
|
||||
|
||||
1. Open pgAdmin or your database GUI
|
||||
2. Connect to your `portfolio` database
|
||||
3. Open Query Tool
|
||||
4. Copy and paste the contents of `create_activity_status.sql`
|
||||
5. Execute the query
|
||||
|
||||
## Verifying Migration
|
||||
|
||||
After running the migration, verify it was successful:
|
||||
|
||||
```bash
|
||||
# Check if table exists
|
||||
psql -d portfolio -c "\dt activity_status"
|
||||
|
||||
# View table structure
|
||||
psql -d portfolio -c "\d activity_status"
|
||||
|
||||
# Check if default row was inserted
|
||||
psql -d portfolio -c "SELECT * FROM activity_status;"
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
id | activity_type | ... | updated_at
|
||||
----+---------------+-----+---------------------------
|
||||
1 | | ... | 2024-01-15 10:30:00+00
|
||||
```
|
||||
|
||||
## Migration: create_activity_status.sql
|
||||
|
||||
**Purpose**: Creates the `activity_status` table for n8n activity feed integration.
|
||||
|
||||
**What it does**:
|
||||
- Creates `activity_status` table with all necessary columns
|
||||
- Inserts a default row with `id = 1`
|
||||
- Sets up automatic `updated_at` timestamp trigger
|
||||
- Adds table comment for documentation
|
||||
|
||||
**Required by**:
|
||||
- `/api/n8n/status` endpoint
|
||||
- `ActivityFeed` component
|
||||
- n8n workflows for status updates
|
||||
|
||||
**Safe to run multiple times**: Yes (uses `IF NOT EXISTS` and `ON CONFLICT`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "relation already exists"
|
||||
Table already exists - migration is already applied. Safe to ignore.
|
||||
|
||||
### "permission denied"
|
||||
Your database user needs CREATE TABLE permissions:
|
||||
```sql
|
||||
GRANT CREATE ON DATABASE portfolio TO your_user;
|
||||
```
|
||||
|
||||
### "database does not exist"
|
||||
Create the database first:
|
||||
```bash
|
||||
createdb portfolio
|
||||
# Or
|
||||
psql -c "CREATE DATABASE portfolio;"
|
||||
```
|
||||
|
||||
### "connection refused"
|
||||
Ensure PostgreSQL is running:
|
||||
```bash
|
||||
# Check status
|
||||
pg_isready
|
||||
|
||||
# Start PostgreSQL (macOS)
|
||||
brew services start postgresql
|
||||
|
||||
# Start PostgreSQL (Linux)
|
||||
sudo systemctl start postgresql
|
||||
```
|
||||
|
||||
## Rolling Back
|
||||
|
||||
To remove the activity_status table:
|
||||
|
||||
```sql
|
||||
DROP TRIGGER IF EXISTS activity_status_updated_at ON activity_status;
|
||||
DROP FUNCTION IF EXISTS update_activity_status_updated_at();
|
||||
DROP TABLE IF EXISTS activity_status;
|
||||
```
|
||||
|
||||
Save this as `rollback_activity_status.sql` and run if needed.
|
||||
|
||||
## Future Migrations
|
||||
|
||||
When adding new migrations:
|
||||
1. Create a new `.sql` file with descriptive name
|
||||
2. Use timestamps in filename: `YYYYMMDD_description.sql`
|
||||
3. Document what it does in this README
|
||||
4. Test on local database first
|
||||
5. Mark as safe/unsafe for production
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2024-01-15
|
||||
**Status**: Required for n8n integration
|
||||
@@ -1,49 +0,0 @@
|
||||
-- Create activity_status table for n8n integration
|
||||
CREATE TABLE IF NOT EXISTS activity_status (
|
||||
id INTEGER PRIMARY KEY DEFAULT 1,
|
||||
activity_type VARCHAR(50),
|
||||
activity_details VARCHAR(255),
|
||||
activity_project VARCHAR(255),
|
||||
activity_language VARCHAR(50),
|
||||
activity_repo VARCHAR(500),
|
||||
music_playing BOOLEAN DEFAULT FALSE,
|
||||
music_track VARCHAR(255),
|
||||
music_artist VARCHAR(255),
|
||||
music_album VARCHAR(255),
|
||||
music_platform VARCHAR(50),
|
||||
music_progress INTEGER,
|
||||
music_album_art VARCHAR(500),
|
||||
watching_title VARCHAR(255),
|
||||
watching_platform VARCHAR(50),
|
||||
watching_type VARCHAR(50),
|
||||
gaming_game VARCHAR(255),
|
||||
gaming_platform VARCHAR(50),
|
||||
gaming_status VARCHAR(50),
|
||||
status_mood VARCHAR(50),
|
||||
status_message VARCHAR(500),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Insert default row
|
||||
INSERT INTO activity_status (id, updated_at)
|
||||
VALUES (1, NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Create function to automatically update updated_at
|
||||
CREATE OR REPLACE FUNCTION update_activity_status_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create trigger for automatic timestamp updates
|
||||
DROP TRIGGER IF EXISTS activity_status_updated_at ON activity_status;
|
||||
CREATE TRIGGER activity_status_updated_at
|
||||
BEFORE UPDATE ON activity_status
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_activity_status_updated_at();
|
||||
|
||||
-- Add helpful comment
|
||||
COMMENT ON TABLE activity_status IS 'Stores real-time activity status from n8n workflows (coding, music, gaming, etc.)';
|
||||
1
prisma/migrations/migration_lock.toml
Normal file
1
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1 @@
|
||||
provider = "postgresql"
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Quick Fix Script for Portfolio Database
|
||||
# This script creates the activity_status table needed for n8n integration
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔧 Portfolio Database Quick Fix"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check if .env.local exists
|
||||
if [ ! -f .env.local ]; then
|
||||
echo -e "${RED}❌ Error: .env.local not found${NC}"
|
||||
echo "Please create .env.local with DATABASE_URL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load DATABASE_URL from .env.local
|
||||
export $(grep -v '^#' .env.local | xargs)
|
||||
|
||||
if [ -z "$DATABASE_URL" ]; then
|
||||
echo -e "${RED}❌ Error: DATABASE_URL not found in .env.local${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Found DATABASE_URL${NC}"
|
||||
echo ""
|
||||
|
||||
# Extract database name from DATABASE_URL
|
||||
DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p')
|
||||
echo "📦 Database: $DB_NAME"
|
||||
echo ""
|
||||
|
||||
# Run the migration
|
||||
echo "🚀 Creating activity_status table..."
|
||||
echo ""
|
||||
|
||||
psql "$DATABASE_URL" -f prisma/migrations/create_activity_status.sql
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ SUCCESS! Migration completed${NC}"
|
||||
echo ""
|
||||
echo "Verifying table..."
|
||||
psql "$DATABASE_URL" -c "\d activity_status" | head -20
|
||||
echo ""
|
||||
echo "Checking default row..."
|
||||
psql "$DATABASE_URL" -c "SELECT id, updated_at FROM activity_status LIMIT 1;"
|
||||
echo ""
|
||||
echo -e "${GREEN}🎉 All done! Your database is ready.${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Restart your Next.js dev server: npm run dev"
|
||||
echo " 2. Visit http://localhost:3000"
|
||||
echo " 3. The activity feed should now work without errors"
|
||||
else
|
||||
echo ""
|
||||
echo -e "${RED}❌ Migration failed${NC}"
|
||||
echo ""
|
||||
echo "Troubleshooting:"
|
||||
echo " 1. Ensure PostgreSQL is running: pg_isready"
|
||||
echo " 2. Check your DATABASE_URL in .env.local"
|
||||
echo " 3. Verify database exists: psql -l | grep $DB_NAME"
|
||||
echo " 4. Try manual migration: psql $DB_NAME -f prisma/migrations/create_activity_status.sql"
|
||||
exit 1
|
||||
fi
|
||||
40
scripts/start-with-migrate.js
Normal file
40
scripts/start-with-migrate.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Container entrypoint: apply Prisma migrations, then start Next server.
|
||||
*
|
||||
* Why:
|
||||
* - In real deployments you want schema changes applied automatically per deploy.
|
||||
* - `prisma migrate deploy` is safe to run multiple times (idempotent).
|
||||
*
|
||||
* Controls:
|
||||
* - Set `SKIP_PRISMA_MIGRATE=true` to skip migrations (emergency / debugging).
|
||||
*/
|
||||
const { spawnSync } = require("node:child_process");
|
||||
|
||||
function run(cmd, args, opts = {}) {
|
||||
const res = spawnSync(cmd, args, {
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
...opts,
|
||||
});
|
||||
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
if (typeof res.status === "number" && res.status !== 0) {
|
||||
// propagate exit code
|
||||
process.exit(res.status);
|
||||
}
|
||||
}
|
||||
|
||||
const skip = String(process.env.SKIP_PRISMA_MIGRATE || "").toLowerCase() === "true";
|
||||
if (!skip) {
|
||||
// Avoid relying on `npx` resolution in minimal runtimes.
|
||||
// We copy `node_modules/prisma` into the runtime image.
|
||||
run("node", ["node_modules/prisma/build/index.js", "migrate", "deploy"]);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("SKIP_PRISMA_MIGRATE=true -> skipping prisma migrate deploy");
|
||||
}
|
||||
|
||||
run("node", ["server.js"]);
|
||||
|
||||
Reference in New Issue
Block a user