Add Turso support: schema SQL + entrypoint skip for cloud DB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Fix /data permissions — Docker volumes are mounted as root by default
|
if [ -n "$TURSO_DATABASE_URL" ]; then
|
||||||
chown -R nextjs:nodejs /data
|
echo "Turso database configured — skipping local migrations."
|
||||||
|
else
|
||||||
|
# Fix /data permissions — Docker volumes are mounted as root by default
|
||||||
|
chown -R nextjs:nodejs /data 2>/dev/null || true
|
||||||
|
|
||||||
# Run Prisma migrations as nextjs user
|
echo "Running SQLite migrations..."
|
||||||
echo "Running database migrations..."
|
DATABASE_URL="${DATABASE_URL:-file:/data/leadflow.db}" \
|
||||||
DATABASE_URL="${DATABASE_URL:-file:/data/leadflow.db}" \
|
|
||||||
su-exec nextjs node node_modules/prisma/build/index.js migrate deploy \
|
su-exec nextjs node node_modules/prisma/build/index.js migrate deploy \
|
||||||
--schema ./prisma/schema.prisma 2>&1 || echo "Migration warning (may already be up to date)"
|
--schema ./prisma/schema.prisma 2>&1 || echo "Migration warning (may already be up to date)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Starting LeadFlow..."
|
echo "Starting LeadFlow..."
|
||||||
exec su-exec nextjs node server.js
|
exec su-exec nextjs node server.js
|
||||||
|
|||||||
102
prisma/turso-schema.sql
Normal file
102
prisma/turso-schema.sql
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
-- LeadFlow schema for Turso (combined migrations)
|
||||||
|
-- Apply with: turso db shell <db-name> < prisma/turso-schema.sql
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "ApiCredential" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"service" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS "ApiCredential_service_key" ON "ApiCredential"("service");
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "Job" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"type" TEXT NOT NULL,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
"config" TEXT NOT NULL DEFAULT '{}',
|
||||||
|
"totalLeads" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"emailsFound" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"error" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "LeadResult" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"jobId" TEXT NOT NULL,
|
||||||
|
"companyName" TEXT,
|
||||||
|
"domain" TEXT,
|
||||||
|
"contactName" TEXT,
|
||||||
|
"contactTitle" TEXT,
|
||||||
|
"email" TEXT,
|
||||||
|
"confidence" REAL,
|
||||||
|
"linkedinUrl" TEXT,
|
||||||
|
"source" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY ("jobId") REFERENCES "Job" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "Lead" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"domain" TEXT,
|
||||||
|
"companyName" TEXT,
|
||||||
|
"contactName" TEXT,
|
||||||
|
"contactTitle" TEXT,
|
||||||
|
"email" TEXT,
|
||||||
|
"linkedinUrl" TEXT,
|
||||||
|
"phone" TEXT,
|
||||||
|
"address" TEXT,
|
||||||
|
"sourceTab" TEXT NOT NULL,
|
||||||
|
"sourceTerm" TEXT,
|
||||||
|
"sourceJobId" TEXT,
|
||||||
|
"serpTitle" TEXT,
|
||||||
|
"serpSnippet" TEXT,
|
||||||
|
"serpRank" INTEGER,
|
||||||
|
"serpUrl" TEXT,
|
||||||
|
"emailConfidence" REAL,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'new',
|
||||||
|
"priority" TEXT NOT NULL DEFAULT 'normal',
|
||||||
|
"notes" TEXT,
|
||||||
|
"tags" TEXT,
|
||||||
|
"country" TEXT,
|
||||||
|
"headcount" TEXT,
|
||||||
|
"industry" TEXT,
|
||||||
|
"description" TEXT,
|
||||||
|
"companyType" TEXT,
|
||||||
|
"topics" TEXT,
|
||||||
|
"salesScore" INTEGER,
|
||||||
|
"salesReason" TEXT,
|
||||||
|
"offerPackage" TEXT,
|
||||||
|
"approved" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"approvedAt" DATETIME,
|
||||||
|
"capturedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"contactedAt" DATETIME,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_domain_idx" ON "Lead"("domain");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_status_idx" ON "Lead"("status");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_sourceTab_idx" ON "Lead"("sourceTab");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_capturedAt_idx" ON "Lead"("capturedAt");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_email_idx" ON "Lead"("email");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_approved_idx" ON "Lead"("approved");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Lead_companyType_idx" ON "Lead"("companyType");
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "LeadEvent" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"leadId" TEXT NOT NULL,
|
||||||
|
"event" TEXT NOT NULL,
|
||||||
|
"at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY ("leadId") REFERENCES "Lead" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "LeadEvent_leadId_idx" ON "LeadEvent"("leadId");
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "SearchHistory" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"query" TEXT NOT NULL,
|
||||||
|
"region" TEXT NOT NULL,
|
||||||
|
"searchMode" TEXT NOT NULL,
|
||||||
|
"executedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "SearchHistory_searchMode_idx" ON "SearchHistory"("searchMode");
|
||||||
|
CREATE INDEX IF NOT EXISTS "SearchHistory_executedAt_idx" ON "SearchHistory"("executedAt");
|
||||||
Reference in New Issue
Block a user