Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ terraform.rc

# Optional: ignore plan files saved before destroying Terraform configuration
# Uncomment the line below if you want to ignore planout files.
# planout
# planout

# Service account keys
key.json
*.json.key
*-key.json
service-account-key*.json
37 changes: 24 additions & 13 deletions backend/execution-service/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,38 @@ const PORT = process.env.PORT || 8080;

async function main() {
let worker;

if (useGcp) {
console.log("Starting Pub/Sub worker...");
worker = new PubSubWorker();
} else {
console.log("Starting RabbitMQ worker...");
worker = new RabbitMQWorker();
}

await worker.start();

let server = null;

// Start HTTP server first for Cloud Run health checks
if (useGcp) {
// Create a minimal HTTP server for GCP Cloud Run health checks
server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
data: 'Hello World!',
status: 'ok',
}));
});
server.listen(PORT);
server.listen(PORT, () => {
console.log(`HTTP server listening on port ${PORT}`);
});
}


try {
if (useGcp) {
console.log("Starting Pub/Sub worker...");
worker = new PubSubWorker();
} else {
console.log("Starting RabbitMQ worker...");
worker = new RabbitMQWorker();
}


worker.start().catch((error) => {
console.error("Failed to start worker:", error);
});
} catch (error) {
console.error("Error initializing worker:", error);
}

// Graceful shutdown
Expand Down
4 changes: 2 additions & 2 deletions backend/question-service/src/config/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ const connectDB = async () => {
console.log(`Database contains ${questionCount} questions`)
}
} catch (error) {
console.log(error)
process.exit(1)
console.log("MongoDB connection error:", error)
throw error
}
}

Expand Down
30 changes: 24 additions & 6 deletions backend/question-service/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ const connectDB = require('./config/db')
const { kafkaManager } = require('./config/kafka')
const { pubsubManager } = require('./config/pubsub')

connectDB()

const app = express()

app.use(
cors({
origin: process.env.WEB_BASE_URL,
origin: process.env.WEB_BASE_URL || "*", // Allow all origins if not set
credentials: true,
optionsSuccessStatus: 200,
}),
Expand All @@ -20,16 +18,36 @@ app.use(
app.use(express.json())
app.use(express.urlencoded({ extended: false }))

// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: 'question-service' })
})

const PORT = process.env.PORT || 8003
const useGcp = !!process.env.PUBSUB_PROJECT_ID;

app.listen(PORT, async () => {
console.log(`Question service is running on port ${PORT}...`)

if (useGcp) {
await pubsubManager.setupSubscribers();

if (process.env.MONGO_URI) {
connectDB().catch((err) => {
console.log("MongoDB connection error:", err);
console.log("Server is running but MongoDB is not connected");
});
} else {
await kafkaManager.setupSubscribers();
console.log("MONGO_URI not set, skipping MongoDB connection");
}

// Setup subscribers
try {
if (useGcp) {
await pubsubManager.setupSubscribers();
} else {
await kafkaManager.setupSubscribers();
}
} catch (error) {
console.error("Error setting up subscribers:", error);
}
})

Expand Down
75 changes: 46 additions & 29 deletions backend/user-service/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,54 @@ app.use(
);
app.use(express.json());

// Health check endpoint
app.get("/health", (req, res) => {
res.json({ status: "ok", service: "user-service" });
});

// Routes
app.use("/v1", authRoutes);

// Connect to MongoDB
mongoose
.connect(process.env.MONGO_URI)
.then(() => console.log("MongoDB connected"))
.then(async () => {
console.log("MongoDB connected");

const adminUsername = process.env.ADMIN_USERNAME;
const adminEmail = process.env.ADMIN_EMAIL;
const adminPassword = process.env.ADMIN_PW;
app.listen(PORT, () => {
console.log(`Server started on port ${PORT}`);

if (process.env.MONGO_URI) {
mongoose
.connect(process.env.MONGO_URI)
.then(() => {
console.log("MongoDB connected");

// Create admin user if needed
const adminUsername = process.env.ADMIN_USERNAME;
const adminEmail = process.env.ADMIN_EMAIL;
const adminPassword = process.env.ADMIN_PW;

const existingAdmin = await User.findOne({ username: adminUsername });

if (!existingAdmin) {
const hashedPassword = await bcrypt.hash(adminPassword, 10);
const admin = new User({
username: adminUsername,
email: adminEmail,
password: hashedPassword,
role: "admin",
verified: true
if (adminUsername && adminEmail && adminPassword) {
User.findOne({ username: adminUsername })
.then(async (existingAdmin) => {
if (!existingAdmin) {
const hashedPassword = await bcrypt.hash(adminPassword, 10);
const admin = new User({
username: adminUsername,
email: adminEmail,
password: hashedPassword,
role: "admin",
verified: true
});
await admin.save();
console.log("Default admin user created");
} else {
console.log("Admin user already exists");
}
})
.catch((err) => console.log("Error creating admin user:", err));
}
})
.catch((err) => {
console.log("MongoDB connection error:", err);
console.log("Server is running but MongoDB is not connected");
});
await admin.save();
console.log("Default admin user created");
} else {
console.log("Admin user already exists");
}
})
.catch((err) => console.log("MongoDB connection error:", err));

app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
} else {
console.log("MONGO_URI not set, skipping MongoDB connection");
}
});
28 changes: 28 additions & 0 deletions setup-iam.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
$PROJECT_ID = "your-project-id"
$SA_EMAIL = "peerprep-cd-sa@${PROJECT_ID}.iam.gserviceaccount.com"

Write-Host "Setting up IAM for project: $PROJECT_ID" -ForegroundColor Green
Write-Host "Service Account: $SA_EMAIL" -ForegroundColor Green

# Cloud Run Admin
Write-Host "`nGranting Cloud Run Admin role..." -ForegroundColor Yellow
gcloud projects add-iam-policy-binding $PROJECT_ID `
--member="serviceAccount:$SA_EMAIL" `
--role="roles/run.admin"

# Artifact Registry Writer
Write-Host "`nGranting Artifact Registry Writer role..." -ForegroundColor Yellow
gcloud projects add-iam-policy-binding $PROJECT_ID `
--member="serviceAccount:$SA_EMAIL" `
--role="roles/artifactregistry.writer"

# Service Account User (to use the service account)
Write-Host "`nGranting Service Account User role..." -ForegroundColor Yellow
gcloud projects add-iam-policy-binding $PROJECT_ID `
--member="serviceAccount:$SA_EMAIL" `
--role="roles/iam.serviceAccountUser"

Write-Host "`nIAM setup complete!" -ForegroundColor Green
Write-Host "`nNext step: Create service account key with:" -ForegroundColor Cyan
Write-Host " gcloud iam service-accounts keys create key.json --iam-account=$SA_EMAIL" -ForegroundColor White

17 changes: 17 additions & 0 deletions setup-iam.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PROJECT_ID="your-project-id"
SA_EMAIL="peerprep-cd-sa@${PROJECT_ID}.iam.gserviceaccount.com"

# Cloud Run Admin
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/run.admin"

# Artifact Registry Writer
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/artifactregistry.writer"

# Service Account User (to use the service account)
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/iam.serviceAccountUser"
1 change: 1 addition & 0 deletions terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 70 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,36 @@ resource "google_cloud_run_v2_service" "user_service" {
}
}

env {
name = "ADMIN_EMAIL"
value_source {
secret_key_ref {
secret = "admin_email"
version = "latest"
}
}
}

env {
name = "ADMIN_USERNAME"
value_source {
secret_key_ref {
secret = "admin_username"
version = "latest"
}
}
}

env {
name = "ADMIN_PW"
value_source {
secret_key_ref {
secret = "admin_pw"
version = "latest"
}
}
}

resources {
limits = {
cpu = "1"
Expand Down Expand Up @@ -296,6 +326,46 @@ resource "google_cloud_run_v2_service" "question_service" {
value = var.project_id
}

env {
name = "CLOUDINARY_CLOUD_NAME"
value_source {
secret_key_ref {
secret = "cloudinary_cloud_name"
version = "latest"
}
}
}

env {
name = "CLOUDINARY_API_KEY"
value_source {
secret_key_ref {
secret = "cloudinary_api_key"
version = "latest"
}
}
}

env {
name = "CLOUDINARY_API_SECRET"
value_source {
secret_key_ref {
secret = "cloudinary_api_secret"
version = "latest"
}
}
}

env {
name = "JWT_SECRET"
value_source {
secret_key_ref {
secret = "jwt_secret"
version = "latest"
}
}
}

resources {
limits = {
cpu = "1"
Expand Down