Skip to content
Open
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
69 changes: 69 additions & 0 deletions sticky-notes-whiteboard/.github/workflows/ci-cd-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: CI/CD Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
# Step 1: Check out the code
- name: Checkout Code
uses: actions/checkout@v3

# Step 2: Set up Node.js for the project
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '22'

# Step 3: Cache dependencies
- name: Cache Node.js modules
uses: actions/cache@v3
with:
path: |
~/.npm
frontend/node_modules
backend/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

# Step 4: Install dependencies and run tests
- name: Install dependencies and run tests
run: |
cd frontend && npm install && npm run test
cd ../backend && npm install && npm run test

# Step 5: Build the frontend and backend
- name: Build Frontend and Backend
run: |
cd frontend && npm run build
cd ../backend && npm run build

# Step 6: Run security checks
- name: Run Security Checks
run: |
cd frontend && npm audit --audit-level=moderate
cd ../backend && npm audit --audit-level=moderate

# Step 7: Deploy to server
- name: Deploy to Server
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd
export PORT=${{ secrets.PORT }}
export POSTGRES_DATABASE_URL=${{ secrets.POSTGRES_DATABASE_URL }}
export JWT_SECRET=${{ secrets.JWT_SECRET }}
docker compose down
docker compose up -d --build
186 changes: 186 additions & 0 deletions sticky-notes-whiteboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Sticky Notes Whiteboard Application

## Overview

This project is a full-stack web application that allows users to create, view, and manage sticky notes on a virtual whiteboard. It includes:

- A **frontend** built with React.
- A **backend** built with Node.js and Express.
- **NGINX** for serving the frontend and routing API requests to the backend.
- **SSL** setup using Let's Encrypt for secure connections.
- Dockerized deployment for consistent environment management.

---

## Project Structure

```plaintext
sticky-notes-whiteboard/
├── backend/
│ ├── Dockerfile
│ ├── index.js
│ ├── package.json
│ └── .env
├── frontend/
│ ├── Dockerfile
│ ├── src/
│ ├── nginx.conf
│ ├── public/
│ └── package.json
├── docker-compose.yml
└── README.md

```
## Prerequisites

- Docker and Docker Compose installed on your local machine.
- A domain name (for production with SSL).
- (Optional) Certbot for generating SSL certificates.


## Setup Instructions

### Local Development

#### Clone the repository:
```bash
git clone https://github.com/your-username/sticky-notes-whiteboard.git
cd sticky-notes-whiteboard
```
#### Set up environment variables:
1. Create a `.env` file inside the `backend/` directory with the following content:

```plaintext
PORT=4000
POSTGRES_DATABASE_URL=your_database_url
JWT_SECRET=your_jwt_secret
```

#### Start the application:
1. Run the following command to build and start the application:

```bash
docker compose up --build
```

#### Access the application:
- **Frontend:** [http://localhost:3000](http://localhost:3000)
- **Backend API:** [http://localhost:4000/api](http://localhost:4000)

## Production Deployment

1. Replace the domain placeholder (`your-domain.com`) in `nginx.conf` with your actual domain.
2. Generate SSL certificates using Let's Encrypt:
```bash
sudo certbot certonly --standalone -d your-domain.com
```
3. Run the following command to build and start the application:

```bash
docker compose up --build
```

## CI/CD Pipeline Details

This project includes a GitHub Actions workflow for CI/CD. The pipeline:

- Runs tests for both frontend and backend.
- Builds Docker images for frontend and backend.
- Deploys updated services using Docker Compose.

### Workflow File (`.github/workflows/ci-cd-pipeline.yml`)

```bash
name: CI/CD Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
#Step 1: Check out the code
- name: Checkout Code
uses: actions/checkout@v3

# Step 2: Set up Node.js for the project
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '22'

# Step 3: Cache Node.js modules for faster builds
- name: Cache Node.js modules
uses: actions/cache@v3
with:
path: |
~/.npm
frontend/node_modules
backend/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

# Step 4: Install dependencies and run tests
- name: Install dependencies and run tests
run: |
cd frontend && npm install && npm run test
cd ../backend && npm install && npm run test

# Step 5: Build the frontend and backend
- name: Build Frontend and Backend
run: |
cd frontend && npm run build
cd ../backend && npm run build

# Step 6: Run security checks
- name: Run Security Checks
run: |
cd frontend && npm audit --audit-level=moderate
cd ../backend && npm audit --audit-level=moderate

# Step 7: Deploy to server
- name: Deploy to Server
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /
export PORT=${{ secrets.PORT }}
export POSTGRES_DATABASE_URL=${{ secrets.POSTGRES_DATABASE_URL }}
export JWT_SECRET=${{ secrets.JWT_SECRET }}
docker compose down
docker compose up -d --build

```

## Backend API Endpoints

| Method | Endpoint | Description |
|--------|------------------------|---------------------------|
| POST | /api/user/login | Logs in a user |
| POST | /api/flows | Creates a sticky note |
| GET | /api/flows | Fetches all sticky notes |
| DELETE | /api/flows/:id | Deletes a sticky note |

## DevOps Practices

- **Docker**: Used to containerize the application for consistent deployment.
- **NGINX**: Configured to handle routing and SSL.
- **GitHub Actions**: Automates testing and deployment.
- **Secrets Management**: Securely stores sensitive data using GitHub Secrets.

## Challenges and Solutions

- **SSL Setup**: Configured with Let's Encrypt to secure the app.
- **Environment Consistency**: Docker ensures a consistent runtime across environments.
- **Routing Issues**: Resolved with a custom NGINX configuration.
2 changes: 2 additions & 0 deletions sticky-notes-whiteboard/backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.env
6 changes: 6 additions & 0 deletions sticky-notes-whiteboard/backend/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

# Run linting and formatting checks before committing
npm run lint
npm run format:check
5 changes: 5 additions & 0 deletions sticky-notes-whiteboard/backend/.husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

# Run tests and ensure code is ready to be pushed
npm run test
24 changes: 24 additions & 0 deletions sticky-notes-whiteboard/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Use a lightweight Node.js image
FROM node:18-alpine

# Set the working directory inside the container
WORKDIR /app

# Copy the rest of the application code
COPY . .

# Install production dependencies
RUN npm install && npm install --save-dev husky

# Set environment variables from .env during runtime
ENV PORT=4000

# Expose the port the application listens on
EXPOSE 4000

# Create a non-root user for running the app
RUN adduser -D appuser && chown -R appuser /app
USER appuser

# Command to run the application
CMD [ "npm","start" ]
70 changes: 70 additions & 0 deletions sticky-notes-whiteboard/backend/controllers/flowController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const { client } = require("../db/config");

const createFlow = async (req, res) => {
const { content, title} = req.body;
const userId = req.user.id; // Ensure you have user authentication middleware

try {
// Insert flow with position data
const result = await client.query(
"INSERT INTO flows (user_id, content, title) VALUES ($1, $2, $3) RETURNING id",
[userId, content, title]
);
res.status(201).json({ id: result.rows[0].id });
} catch (err) {
res.status(500).json({ message: "Error creating flow" });
}
};

const getFlows = async (req, res) => {
const userId = req.user.id;
try {
const result = await client.query(
"SELECT * FROM flows WHERE user_id = $1",
[userId]
);
res.status(200).json(result.rows);
} catch (err) {
res.status(500).json({ message: "Error fetching flows" });
}
};

const getFlowById = async (req, res) => {
const { id } = req.params;
const userId = req.user.id;

try {
const result = await client.query(
"SELECT * FROM flows WHERE id = $1 AND user_id = $2",
[id, userId]
);

if (result.rows.length === 0)
return res.status(404).json({ message: "Flow not found" });
res.status(200).json(result.rows[0]);
} catch (err) {
res.status(500).json({ message: "Error fetching flow" });
}
};


const deleteFlow = async (req, res) => {
const { id } = req.params;
const userId = req.user.id;

try {
const result = await client.query(
"DELETE FROM flows WHERE id = $1 AND user_id = $2",
[id, userId]
);

if (result.rowCount === 0) {
return res.status(404).json({ message: "Flow not found" });
}
res.status(200).json({message:"Deleted Node Successfully"});
} catch (err) {
res.status(500).json({ message: "Error deleting flow" });
}
};

module.exports = { createFlow, getFlows, getFlowById, deleteFlow };
Loading