pivot: migrate V1 mobile from Docker to Netlify serverless
- Switch Prisma schema from SQLite to PostgreSQL - Add netlify.toml for Netlify deployment config - Add @netlify/plugin-nextjs for serverless Next.js - Remove Docker build files (Dockerfile, docker-compose, start.sh) - Remove auto-healer and scripts directories - Update next.config.ts (remove standalone output) - Database: falah_mobile_v1 on Contabo Postgres
This commit is contained in:
+305
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* seed-data.mjs — Seed V1 Mobile SQLite DB with courses, forum, and Islamic finance data
|
||||
* Idempotent: safe to run multiple times (upserts/checks before creating)
|
||||
* Run via: docker cp seed-data.mjs falah_falah-mobile.X:/app/data/ && docker exec falah_falah-mobile.X node /app/data/seed-data.mjs
|
||||
*/
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const DEMO_EMAIL = "demo@falahos.my";
|
||||
|
||||
async function seed() {
|
||||
console.log("🌱 Starting V1 Mobile seed...");
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// 1. FORUM CATEGORIES
|
||||
// ═══════════════════════════════════════════
|
||||
const categories = [
|
||||
{ name: "General Discussion", description: "General Islamic discussions and community topics", icon: "MessageSquare", order: 1 },
|
||||
{ name: "Quran & Hadith", description: "Study and discussion of Quranic verses and hadith", icon: "BookOpen", order: 2 },
|
||||
{ name: "Fiqh & Rulings", description: "Islamic jurisprudence and daily rulings", icon: "Scale", order: 3 },
|
||||
{ name: "Family & Marriage", description: "Family relationships, marriage, and parenting in Islam", icon: "Heart", order: 4 },
|
||||
{ name: "Finance & Economy", description: "Islamic finance, halal investing, and economic topics", icon: "TrendingUp", order: 5 },
|
||||
{ name: "Announcements", description: "Official Falah platform announcements", icon: "Megaphone", order: 0 },
|
||||
];
|
||||
|
||||
let categoryIds = {};
|
||||
for (const cat of categories) {
|
||||
const existing = await prisma.forumCategory.findUnique({ where: { name: cat.name } });
|
||||
if (existing) {
|
||||
categoryIds[cat.name] = existing.id;
|
||||
console.log(` ℹ️ Forum category "${cat.name}" already exists (id: ${existing.id})`);
|
||||
} else {
|
||||
const created = await prisma.forumCategory.create({ data: cat });
|
||||
categoryIds[cat.name] = created.id;
|
||||
console.log(` ✅ Created forum category: "${cat.name}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// 2. DEMO USER (ensure exists with proper balance)
|
||||
// ═══════════════════════════════════════════
|
||||
let demoUser = await prisma.user.findUnique({ where: { email: DEMO_EMAIL } });
|
||||
if (!demoUser) {
|
||||
demoUser = await prisma.user.create({
|
||||
data: {
|
||||
email: DEMO_EMAIL,
|
||||
name: "Demo User",
|
||||
flhBalance: 100000,
|
||||
isPremium: true,
|
||||
isPro: true,
|
||||
},
|
||||
});
|
||||
console.log(` ✅ Created demo user: ${DEMO_EMAIL} (id: ${demoUser.id})`);
|
||||
} else {
|
||||
// Update premium status and balance
|
||||
demoUser = await prisma.user.update({
|
||||
where: { email: DEMO_EMAIL },
|
||||
data: { flhBalance: 100000, isPremium: true, isPro: true },
|
||||
});
|
||||
console.log(` ℹ️ Demo user exists: balance=${demoUser.flhBalance}, premium=${demoUser.isPremium}`);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// 3. FORUM THREADS
|
||||
// ═══════════════════════════════════════════
|
||||
const threads = [
|
||||
{
|
||||
title: "Welcome to Falah Community!",
|
||||
content: "Assalamu alaikum! Welcome to our community forum. This is a place for meaningful discussions, seeking knowledge, and supporting one another. Please introduce yourself!",
|
||||
category: "General Discussion",
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
title: "The Importance of Daily Dhikr",
|
||||
content: "Seeking reminders on the importance of daily remembrance of Allah. What are your favorite adhkar and how do you maintain consistency?",
|
||||
category: "General Discussion",
|
||||
pinned: false,
|
||||
},
|
||||
{
|
||||
title: "Tips for Memorizing Juz Amma",
|
||||
content: "I have started memorizing Juz Amma and would love to hear tips and techniques that worked for others. Jazakum Allah khair!",
|
||||
category: "Quran & Hadith",
|
||||
pinned: false,
|
||||
},
|
||||
{
|
||||
title: "Ruling on Cryptocurrency in Islam",
|
||||
content: "Assalamu alaikum, I would like to understand the Islamic ruling on cryptocurrency trading. Is it considered halal? What do the scholars say about Bitcoin and other digital assets?",
|
||||
category: "Fiqh & Rulings",
|
||||
pinned: false,
|
||||
},
|
||||
{
|
||||
title: "Strengthening Family Bonds in Islam",
|
||||
content: "How do we strengthen our family relationships according to Islamic teachings? Looking for practical advice on being better spouses and parents.",
|
||||
category: "Family & Marriage",
|
||||
pinned: false,
|
||||
},
|
||||
];
|
||||
|
||||
for (const t of threads) {
|
||||
const catId = categoryIds[t.category];
|
||||
if (!catId) continue;
|
||||
// Check if a thread with this title already exists
|
||||
const existing = await prisma.forumThread.findFirst({ where: { title: t.title } });
|
||||
if (existing) {
|
||||
console.log(` ℹ️ Forum thread "${t.title}" already exists`);
|
||||
continue;
|
||||
}
|
||||
await prisma.forumThread.create({
|
||||
data: {
|
||||
title: t.title,
|
||||
content: t.content,
|
||||
categoryId: catId,
|
||||
authorId: demoUser.id,
|
||||
pinned: t.pinned,
|
||||
shariahStatus: "approved",
|
||||
},
|
||||
});
|
||||
console.log(` ✅ Created forum thread: "${t.title}"`);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// 4. LEARN COURSES
|
||||
// ═══════════════════════════════════════════
|
||||
const courses = [
|
||||
{
|
||||
slug: "intro-islamic-finance",
|
||||
title: "Introduction to Islamic Finance",
|
||||
author: "Falah Academy",
|
||||
description: "Learn the fundamentals of Islamic finance including the prohibition of riba (interest), the principles of halal investing, and the key contracts used in Islamic banking and finance.",
|
||||
difficulty: "beginner",
|
||||
published: true,
|
||||
moduleCount: 4,
|
||||
totalMinutes: 30,
|
||||
giteaPath: "courses/intro-islamic-finance",
|
||||
},
|
||||
{
|
||||
slug: "zakat-made-easy",
|
||||
title: "Zakat Made Easy",
|
||||
author: "Falah Academy",
|
||||
description: "A comprehensive guide to understanding and calculating Zakat. Learn about nisab thresholds, different types of wealth, and how to fulfill this important pillar of Islam.",
|
||||
difficulty: "beginner",
|
||||
subscriptionOnly: false,
|
||||
published: true,
|
||||
moduleCount: 5,
|
||||
totalMinutes: 40,
|
||||
giteaPath: "courses/zakat-made-easy",
|
||||
},
|
||||
{
|
||||
slug: "fiqh-of-salah",
|
||||
title: "Fiqh of Salah",
|
||||
author: "Falah Academy",
|
||||
description: "A detailed study of the Islamic prayer (salah) according to the Quran and Sunnah. Covers conditions, pillars, obligations, and common mistakes in prayer.",
|
||||
difficulty: "intermediate",
|
||||
published: true,
|
||||
moduleCount: 6,
|
||||
totalMinutes: 50,
|
||||
giteaPath: "courses/fiqh-of-salah",
|
||||
},
|
||||
{
|
||||
slug: "waqf-endowment-guide",
|
||||
title: "Waqf Endowment Guide",
|
||||
author: "Falah Academy",
|
||||
description: "Understand the concept of Waqf (Islamic endowment) and how to set up perpetual charitable endowments that generate ongoing rewards.",
|
||||
difficulty: "intermediate",
|
||||
published: true,
|
||||
moduleCount: 3,
|
||||
totalMinutes: 25,
|
||||
giteaPath: "courses/waqf-endowment-guide",
|
||||
},
|
||||
{
|
||||
slug: "sadaqah-and-community",
|
||||
title: "Sadaqah & Community Giving",
|
||||
author: "Falah Academy",
|
||||
description: "Explore the virtues of voluntary charity (sadaqah) in Islam and learn how to make the most impact through strategic community giving.",
|
||||
difficulty: "beginner",
|
||||
published: true,
|
||||
moduleCount: 3,
|
||||
totalMinutes: 20,
|
||||
giteaPath: "courses/sadaqah-and-community",
|
||||
},
|
||||
];
|
||||
|
||||
for (const course of courses) {
|
||||
const existing = await prisma.learnCourse.findUnique({ where: { slug: course.slug } });
|
||||
if (existing) {
|
||||
console.log(` ℹ️ Course "${course.title}" already exists`);
|
||||
continue;
|
||||
}
|
||||
await prisma.learnCourse.create({ data: course });
|
||||
console.log(` ✅ Created course: "${course.title}"`);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// 5. AUTO-ENROLL DEMO USER IN ALL COURSES
|
||||
// ═══════════════════════════════════════════
|
||||
const allCourses = await prisma.learnCourse.findMany({ where: { published: true } });
|
||||
for (const course of allCourses) {
|
||||
const existing = await prisma.learnEnrollment.findUnique({
|
||||
where: { userId_courseId: { userId: demoUser.id, courseId: course.id } },
|
||||
});
|
||||
if (!existing) {
|
||||
await prisma.learnEnrollment.create({
|
||||
data: {
|
||||
userId: demoUser.id,
|
||||
courseId: course.id,
|
||||
purchaseType: "free",
|
||||
},
|
||||
});
|
||||
console.log(` ✅ Enrolled demo user in course: \"${course.title}\"`);
|
||||
} else {
|
||||
console.log(` ℹ️ Demo already enrolled in: \"${course.title}\"`);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// 6. LEARN MODULES (for each course)
|
||||
// ═══════════════════════════════════════════
|
||||
const modules = {
|
||||
"intro-islamic-finance": [
|
||||
{ slug: "what-is-riba", title: "What is Riba?", keyTakeaway: "Riba (interest) is strictly prohibited in Islam. Understand the different types and why it matters.", duration: 8 },
|
||||
{ slug: "halal-investing", title: "Halal Investing Principles", keyTakeaway: "Investments must avoid riba, gharar (excessive uncertainty), and haram industries.", duration: 7 },
|
||||
{ slug: "islamic-contracts", title: "Islamic Contracts Overview", keyTakeaway: "Key contracts include Murabaha (cost-plus), Musharakah (partnership), and Ijarah (leasing).", duration: 8 },
|
||||
{ slug: "modern-fintech", title: "Modern Islamic Fintech", keyTakeaway: "Technology is making Islamic finance more accessible through digital wallets, tokenization, and smart contracts.", duration: 7 },
|
||||
],
|
||||
"zakat-made-easy": [
|
||||
{ slug: "what-is-zakat", title: "What is Zakat?", keyTakeaway: "Zakat is the third pillar of Islam — an obligatory charity on wealth that meets nisab threshold.", duration: 8 },
|
||||
{ slug: "calculating-nisab", title: "Calculating Nisab", keyTakeaway: "Nisab is the minimum amount of wealth before Zakat is due, equivalent to 85g of gold or 595g of silver.", duration: 8 },
|
||||
{ slug: "types-of-wealth", title: "Types of Zakatable Wealth", keyTakeaway: "Zakat applies to cash, gold/silver, business inventory, agricultural produce, livestock, and investments.", duration: 8 },
|
||||
{ slug: "zakat-calculation", title: "Calculating Your Zakat", keyTakeaway: "Zakat is 2.5% of qualifying wealth held for one lunar year (hawl).", duration: 8 },
|
||||
{ slug: "distributing-zakat", title: "Distributing Your Zakat", keyTakeaway: "Zakat must be given to 8 eligible categories, prioritizing local needs.", duration: 8 },
|
||||
],
|
||||
"fiqh-of-salah": [
|
||||
{ slug: "conditions-of-salah", title: "Conditions of Salah", keyTakeaway: "Salah has 9 preconditions including purity, facing qibla, and proper covering.", duration: 8 },
|
||||
{ slug: "pillars-of-salah", title: "Pillars of Salah", keyTakeaway: "The 14 pillars of salah are essential — missing any invalidates the prayer.", duration: 8 },
|
||||
{ slug: "obligations-of-salah", title: "Obligations of Salah", keyTakeaway: "The wajibaat (obligatory acts) of salah must be performed; intentional omission requires sujud al-sahw.", duration: 8 },
|
||||
{ slug: "sunnah-practices", title: "Sunnah Practices", keyTakeaway: "Following the sunnah practices of the Prophet ﷺ in salah increases reward and perfects the prayer.", duration: 9 },
|
||||
{ slug: "common-mistakes", title: "Common Mistakes in Salah", keyTakeaway: "Common errors include rushing, incorrect recitation, and improper movements — learn to avoid them.", duration: 9 },
|
||||
{ slug: "congregational-prayer", title: "Congregational Prayer", keyTakeaway: "Praying in congregation (jama'ah) is 27 times more rewarding than praying alone.", duration: 8 },
|
||||
],
|
||||
"waqf-endowment-guide": [
|
||||
{ slug: "what-is-waqf", title: "What is Waqf?", keyTakeaway: "Waqf is a perpetual charitable endowment where the principal is preserved and benefits are continuously given.", duration: 8 },
|
||||
{ slug: "types-of-waqf", title: "Types of Waqf", keyTakeaway: "Waqf can be for family (waqf dhurri), charitable (waqf khayri), or combined purposes.", duration: 8 },
|
||||
{ slug: "digital-waqf", title: "Waqf in the Digital Age", keyTakeaway: "Digital waqf allows perpetual endowments using FLH tokens with automated yield distribution.", duration: 9 },
|
||||
],
|
||||
"sadaqah-and-community": [
|
||||
{ slug: "virtue-of-sadaqah", title: "The Virtue of Sadaqah", keyTakeaway: "Sadaqah purifies wealth, wards off calamities, and brings immense reward, especially when given secretly.", duration: 7 },
|
||||
{ slug: "types-of-sadaqah", title: "Types of Sadaqah", keyTakeaway: "Sadaqah includes both monetary giving and non-monetary acts like a smile or kind word (sadaqah jariyah).", duration: 7 },
|
||||
{ slug: "maximizing-impact", title: "Maximizing Community Impact", keyTakeaway: "Strategic giving through categorized causes (water, education, food, health) amplifies community benefit.", duration: 6 },
|
||||
],
|
||||
};
|
||||
|
||||
for (const [courseSlug, courseModules] of Object.entries(modules)) {
|
||||
const course = await prisma.learnCourse.findUnique({ where: { slug: courseSlug } });
|
||||
if (!course) {
|
||||
console.log(` ⚠️ Course "${courseSlug}" not found, skipping modules`);
|
||||
continue;
|
||||
}
|
||||
for (let i = 0; i < courseModules.length; i++) {
|
||||
const mod = courseModules[i];
|
||||
const existing = await prisma.learnModule.findUnique({
|
||||
where: { courseId_slug: { courseId: course.id, slug: mod.slug } },
|
||||
});
|
||||
if (existing) {
|
||||
continue;
|
||||
}
|
||||
await prisma.learnModule.create({
|
||||
data: {
|
||||
courseId: course.id,
|
||||
order: i,
|
||||
title: mod.title,
|
||||
slug: mod.slug,
|
||||
keyTakeaway: mod.keyTakeaway,
|
||||
duration: mod.duration,
|
||||
content: `# ${mod.title}\n\n${mod.keyTakeaway}\n\n<!-- Full lesson content to be added in future updates -->`,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(` ✅ Created ${courseModules.length} modules for "${course.title}"`);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// SUMMARY
|
||||
// ═══════════════════════════════════════════
|
||||
const courseCount = await prisma.learnCourse.count();
|
||||
const moduleCount = await prisma.learnModule.count();
|
||||
const threadCount = await prisma.forumThread.count();
|
||||
const categoryCount = await prisma.forumCategory.count();
|
||||
const userCount = await prisma.user.count();
|
||||
|
||||
console.log("\n📊 Seed Summary:");
|
||||
console.log(` Users: ${userCount}`);
|
||||
console.log(` Forum Categories: ${categoryCount}`);
|
||||
console.log(` Forum Threads: ${threadCount}`);
|
||||
console.log(` Courses: ${courseCount}`);
|
||||
console.log(` Course Modules: ${moduleCount}`);
|
||||
console.log("✅ Seed complete!");
|
||||
}
|
||||
|
||||
seed()
|
||||
.catch((e) => {
|
||||
console.error("❌ Seed failed:", e.message);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
Reference in New Issue
Block a user