diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index a40aee1718..12907723b7 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -57,7 +57,7 @@ services: - "80:80" - "443:443" volumes: - - $PWD/Caddyfile:/etc/caddy/Caddyfile + - ./Caddyfile:/etc/caddy/Caddyfile - $PWD/site:/srv - caddy_data:/data - caddy_config:/config diff --git a/sample_data/venue.json b/sample_data/venue.json new file mode 100644 index 0000000000..a2b44acb88 --- /dev/null +++ b/sample_data/venue.json @@ -0,0 +1,34 @@ +[ + { + "_id": "6437904485008f171cf29925", + "name": "Unity Foundation", + "description": "A large hall for community events in the Bronx.", + "capacity": 500, + "imageUrl": "https://example.com/bronx-hall.jpg", + "organization": "6437904485008f171cf29924" + }, + { + "_id": "6537904485008f171cf29925", + "name": "Unity Conference Room - Queens", + "description": "A small conference room for meetings in Queens.", + "capacity": 50, + "imageUrl": "https://example.com/queens-conference-room.jpg", + "organization": "6537904485008f171cf29924" + }, + { + "_id": "6637904485008f171cf29925", + "name": "Unity Arena - Staten Island", + "description": "A large outdoor arena for public events in Staten Island.", + "capacity": 2000, + "imageUrl": "https://example.com/staten-island-arena.jpg", + "organization": "6637904485008f171cf29924" + }, + { + "_id": "6737904485008f171cf29925", + "name": "Unity Hall - Brooklyn", + "description": "A community hall in Brooklyn for social events.", + "capacity": 300, + "imageUrl": "https://example.com/brooklyn-hall.jpg", + "organization": "6737904485008f171cf29924" + } +] diff --git a/setup.ts b/setup.ts index c5e5a9cc56..b8ad43ecc3 100644 --- a/setup.ts +++ b/setup.ts @@ -5,7 +5,7 @@ import fs from "fs"; import inquirer from "inquirer"; import path from "path"; import type { ExecException } from "child_process"; -import { exec } from "child_process"; +import { exec, spawn } from "child_process"; import { MongoClient } from "mongodb"; import { MAXIMUM_IMAGE_SIZE_LIMIT_KB } from "./src/constants"; import { @@ -462,6 +462,63 @@ export async function mongoDB(): Promise { } } +/* + For Docker setup +*/ + +async function runDockerComposeWithLogs(): Promise { + // Check if Docker daemon is running + try { + await new Promise((resolve, reject) => { + const dockerCheck = spawn( + process.platform === "win32" ? "docker.exe" : "docker", + ["info"], + { stdio: "ignore" }, + ); + dockerCheck.on("error", reject); + dockerCheck.on("close", (code) => + code === 0 + ? resolve(null) + : reject(new Error("Docker daemon not running")), + ); + }); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + throw new Error( + `Docker daemon is not running. Please start Docker and try again. Details: ${errorMessage}`, + ); + } + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + dockerCompose.kill(); + reject(new Error("Docker compose operation timed out after 5 minutes")); + }, 300000); + + const dockerCompose = spawn( + process.platform === "win32" ? "docker-compose.exe" : "docker-compose", + ["-f", "docker-compose.dev.yaml", "up", "--build", "-d"], + { stdio: "inherit" }, + ); + + dockerCompose.on("error", (error) => { + clearTimeout(timeout); + console.error("Error running docker-compose:", error); + reject(error); + }); + + dockerCompose.on("close", (code) => { + clearTimeout(timeout); + if (code === 0) { + console.log("Docker Compose completed successfully."); + resolve(); + } else { + reject(new Error(`Docker Compose exited with code ${code}`)); + } + }); + }); +} + //Get recaptcha details /** * The function `recaptcha` prompts the user to enter a reCAPTCHA secret key, validates the input, and @@ -917,7 +974,7 @@ async function main(): Promise { type: "confirm", name: "isDockerInstallation", message: "Are you setting up this project using Docker?", - default: false, + default: process.env.MONGO ? false : true, }); if (isDockerInstallation) { @@ -1181,6 +1238,64 @@ async function main(): Promise { console.log( "\nCongratulations! Talawa API has been successfully setup! 🥂🎉", ); + + const { shouldStartDockerContainers } = await inquirer.prompt({ + type: "confirm", + name: "shouldStartDockerContainers", + message: "Do you want to start the Docker containers now?", + default: true, + }); + + const { shouldImportSampleData } = await inquirer.prompt({ + type: "confirm", + name: "shouldImportSampleData", + message: + "Do you want to import Talawa sample data for testing and evaluation purposes?", + default: true, + }); + + if (isDockerInstallation) { + if (shouldStartDockerContainers) { + console.log("Starting docker container..."); + try { + await runDockerComposeWithLogs(); + console.log("Docker containers have been built successfully!"); + // Wait for mongoDB to be ready + console.log("Waiting for mongoDB to be ready..."); + let isConnected = false; + const maxRetries = 30; // 30 seconds timeout + let retryCount = 0; + while (!isConnected) { + if (retryCount >= maxRetries) { + throw new Error( + "Timed out waiting for MongoDB to be ready after 30 seconds", + ); + } + try { + const client = new MongoClient(process.env.MONGO_DB_URL as string); + await client.connect(); + await client.db().command({ ping: 1 }); + client.close(); + isConnected = true; + console.log("MongoDB is ready!"); + } catch (err) { + const error = err instanceof Error ? err.message : String(err); + console.log( + `Waiting for MongoDB to be ready... Retry ${retryCount + 1}/${maxRetries}. Details: ${error}`, + ); + await new Promise((resolve) => setTimeout(resolve, 1000)); + retryCount++; + } + } + + if (shouldImportSampleData) { + await importData(); + } + } catch (err) { + console.log("Some error occurred: " + err); + } + } + } } main(); diff --git a/src/models/SampleData.ts b/src/models/SampleData.ts index 8f09cc609d..ea8c91a3e1 100644 --- a/src/models/SampleData.ts +++ b/src/models/SampleData.ts @@ -11,6 +11,7 @@ export interface InterfaceSampleData extends Document { | "Organization" | "Post" | "Event" + | "Venue" | "User" | "Plugin" | "AppUserProfile"; @@ -28,7 +29,15 @@ const sampleDataSchema = new Schema({ collectionName: { type: String, required: true, - enum: ["Organization", "Post", "Event", "User", "AppUserProfile", "Plugin"], + enum: [ + "Organization", + "Post", + "Event", + "Venue", + "User", + "AppUserProfile", + "Plugin", + ], }, }); diff --git a/src/utilities/loadSampleData.ts b/src/utilities/loadSampleData.ts index 7ff7e666da..d257cde79a 100644 --- a/src/utilities/loadSampleData.ts +++ b/src/utilities/loadSampleData.ts @@ -11,6 +11,7 @@ import { Organization, Post, User, + Venue, } from "../models"; import { RecurrenceRule } from "../models/RecurrenceRule"; @@ -128,6 +129,9 @@ async function insertCollections(collections: string[]): Promise { case "events": await Event.insertMany(docs); break; + case "venue": + await Venue.insertMany(docs); + break; case "recurrenceRules": await RecurrenceRule.insertMany(docs); break; @@ -172,6 +176,7 @@ async function checkCountAfterImport(): Promise { { name: "events", model: Event }, { name: "recurrenceRules", model: RecurrenceRule }, { name: "posts", model: Post }, + { name: "venue", model: Venue }, { name: "appUserProfiles", model: AppUserProfile }, ]; @@ -201,6 +206,7 @@ const collections = [ "organizations", "posts", "events", + "venue", "recurrenceRules", "appUserProfiles", "actionItemCategories", diff --git a/src/utilities/removeSampleOrganizationUtil.ts b/src/utilities/removeSampleOrganizationUtil.ts index 089e97368a..032717557c 100644 --- a/src/utilities/removeSampleOrganizationUtil.ts +++ b/src/utilities/removeSampleOrganizationUtil.ts @@ -4,6 +4,7 @@ import { Organization, Plugin, Post, + Venue, SampleData, User, } from "../models"; @@ -27,6 +28,7 @@ export async function removeSampleOrganization(): Promise { Post, Event, User, + Venue, Plugin, AppUserProfile, }; diff --git a/tests/resolvers/Query/getVolunteerRanks.spec.ts b/tests/resolvers/Query/getVolunteerRanks.spec.ts index cb569ea488..0f5d6973d5 100644 --- a/tests/resolvers/Query/getVolunteerRanks.spec.ts +++ b/tests/resolvers/Query/getVolunteerRanks.spec.ts @@ -58,7 +58,7 @@ describe("resolvers -> Query -> getVolunteerRanks", () => { }, {}, )) as unknown as VolunteerRank[]; - expect(volunteerRanks[0].hoursVolunteered).toEqual(0); + expect(volunteerRanks[0].hoursVolunteered).toEqual(2); expect(volunteerRanks[0].user._id).toEqual(testUser1?._id); expect(volunteerRanks[0].rank).toEqual(1); });