Skip to content

Commit

Permalink
added files
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhinav Gautam authored and Abhinav Gautam committed Oct 8, 2024
0 parents commit 0342313
Show file tree
Hide file tree
Showing 11 changed files with 355 additions and 0 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/deploy-stage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Build and Push Docker Image

on:
push:
branches:
- stage

env:
DOCKER_REPO: puppet-master
DOCKER_IMAGE: stage

jobs:
build:
runs-on: ${{ vars.DOCKER_BUILD_RUNNER }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Log in to Harbor
run: echo "${{ secrets.HARBOR_PASSWORD }}" | docker login https://harbor.internal.codebuckets.in -u '${{ secrets.HARBOR_USER }}' --password-stdin

- name: Build Docker image
run: docker build -t harbor.internal.codebuckets.in/${{ env.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:latest . -f Dockerfile.stage

- name: Push Docker image
run: docker push harbor.internal.codebuckets.in/${{ env.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:latest

deploy:
runs-on: dev-server
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Pull new Image
run: docker compose -f docker-compose-stage.yml pull

- name: Remove old containers
run: docker compose -f docker-compose-stage.yml down || true

- name: Start Container
run: docker compose -f docker-compose-stage.yml up -d
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
.env
.idea
env.json
package-lock.json
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Use the node:20-slim image as base
FROM node:20-slim

# Set the working directory inside the container
WORKDIR /usr/app

# Copy the rest of the application code to the working directory
COPY . .

# Update Repos
RUN apt-get update

# Install Necessary Packages
RUN apt-get install fonts-indic -y

RUN apt-get install fonts-noto-color-emoji -y

# Install Chromium
RUN apt-get install chromium -y

# Install dependencies
RUN npm install

# Expose any necessary ports (if your application listens on any)
EXPOSE 8000

# Command to start the application
CMD ["npm", "run", "start"]
38 changes: 38 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
version: '3.8'

services:
puppet-master:
image: harbor.internal.codebuckets.in/puppet-master/stage
container_name: puppet-master
restart: unless-stopped
ports:
- "127.0.0.1:32001:8000"
networks:
- puppet_master_redis_network
env_file:
- /home/ENV/puppet-master/.env
depends_on:
- redis

redis:
image: redis:6.2.14-bookworm
container_name: puppet_master_redis
restart: unless-stopped
networks:
- puppet_master_redis_network
command:
- /bin/sh
- -c
- redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
volumes:
- puppet_master_redis_volume:/var/lib/redis/data
- /home/ENV/puppet-master/redis.conf:/usr/local/etc/redis/redis.conf:ro
env_file:
- /home/ENV/puppet-master/redis.env

networks:
puppet_master_redis_network:

volumes:
puppet_master_redis_volume:
external: true
32 changes: 32 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "puppet-master",
"version": "1.0.0",
"description": "puppeteer server",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "tsc && node dist/app.js"
},
"dependencies": {
"@types/puppeteer": "^5.4.7",
"body-parser": "^1.20.3",
"bullmq": "^5.17.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"puppeteer": "21.1.0",
"uuid": "^10.0.0"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/uuid": "^10.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.6.2"
},
"keywords": [
"puppeteer"
],
"author": "Abhinav Gautam",
"license": "MIT"
}
36 changes: 36 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import express, { Express, Request, Response, NextFunction } from "express";
import bodyParser from "body-parser";
import cors from "cors";
import queueRoute from "./routes/queue-route";
import handleError from "./middleware/handle-error";

const app: Express = express();
const port = process.env.PORT || 8000;

app
.use(cors())
.use(
bodyParser.urlencoded({
limit: "100mb",
extended: true,
parameterLimit: 50000,
})
)
.use(bodyParser.json({ limit: "100mb" }))
.use(queueRoute);

app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
handleError(err, res);
});

app.listen(port, () => {
console.log(`[server]: Server is running at http://localhost:${port}`);
});

process
.on("unhandledRejection", (reason, p) => {
console.error(reason, "Unhandled Rejection at Promise", p);
})
.on("uncaughtException", (err) => {
console.error(err, "Uncaught Exception thrown");
});
21 changes: 21 additions & 0 deletions src/middleware/handle-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
*
* This middleware checks the centralised error handling. All the error thrown by the controllers is handled
* by this middleware.
*
*
* @param {*} err -> Custom Error Handler
* @param {*} res -> Express response object
*/
import { Response } from "express";

const handleError = (err: Error, res: Response) => {
const { message } = err;
res.status(500).json({
status: "failure",
statusCode: 500,
message,
});
};

export default handleError;
45 changes: 45 additions & 0 deletions src/routes/queue-route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import express, { Request, Response } from "express";
const router = express.Router();
import { v4 } from "uuid";
import { queue, queueEvents}from "../utils/queue-client";
import {Job} from "bullmq";


type response = {
path: string
}

router.post("/pdf", async (req: Request, res: Response) => {
try {
let apiKey = req.headers["x-api-key"];
if (apiKey != process.env.API_KEY) {
throw new Error("Invalid Api Key");
}
const { task } = req.body;

let job: Job|undefined = await queue.add(v4(),task);

await job.waitUntilFinished(queueEvents);

if(job && job.id) {
job = await Job.fromId(queue, job.id);
}

if(job) {
let state = await job.getState()
let response: response = job.returnvalue;
res.download(response.path);
}

throw new Error("Some Error Occurred While Queueing")

} catch (err ) {
res.json({
success: false,
message: err.message,
error: err
});
}
});

export default router;
54 changes: 54 additions & 0 deletions src/utils/queue-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Queue, QueueEvents, Worker, Job } from "bullmq";
import path from "path";

const {NO_OF_WORKERS:numberOfWorkers="1",
WORKER_CONCURRENCY:workerConcurrency="1",
QUEUE_NAME:queueName="pdfQueue"
} = process.env;

const bullConnection = {
connection: {
host: process.env.REDIS_HOST,
username: process.env.REDIS_USERNAME,
password: process.env.REDIS_PASSWORD,
port: process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : undefined,
db: process.env.REDIS_DB_INDEX
? parseInt(process.env.REDIS_DB_INDEX)
: undefined,
},
prefix: process.env.QUEUE_PREFIX,
}

const queue = new Queue(queueName, bullConnection);

const queueEvents = new QueueEvents(queueName, bullConnection);

async function checkQueueHealth(queue: Queue) {
return await queue.getJobCounts();
}

checkQueueHealth(queue).then(jobCounts => {
console.log("Connection Established With Redis Queue", jobCounts);
}).catch(reason => {
console.log("Connection Failed With Redis Queue", reason);
});


const processorFile = path.join(__dirname, 'my_procesor.js');

function createSandboxedWorker() {
return new Worker('pdfQueue', processorFile,{
connection:bullConnection.connection,
concurrency:parseInt(workerConcurrency)
});
}

// Start multiple sandboxed workers
for (let i = 0; i < parseInt(numberOfWorkers); i++) {
createSandboxedWorker();
console.log(`Started worker ${i + 1}`);
}



export {queue,queueEvents};
37 changes: 37 additions & 0 deletions src/utils/sandbox_worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { SandboxedJob } from 'bullmq';
import * as puppeteer from 'puppeteer'
import path from "path";
import { v4 } from "uuid";

const {PDF_FOLDER_PATH: pdfFolderPath=""} = process.env;

module.exports = async (job: SandboxedJob) => {

const payload = job.data;

const browser = await puppeteer.launch(payload.options);
const page = await browser.newPage();
if(payload.content)
await page.setContent(payload.content, payload.pageOptions);

const pdfPath = path.join(pdfFolderPath,`${v4()}.pdf`);
await page.pdf(
{
...payload.pdfOptions,
path: pdfPath
}
);

if(payload.otherPageFunctions) {
for(let fn of Object.keys(payload.otherPageFunctions)) {
// @ts-ignore
await page[fn](...payload.otherPageFunctions[fn])
}
}

await browser.close();

return {
path: pdfPath
}
};
17 changes: 17 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"useUnknownInCatchVariables": false,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}

0 comments on commit 0342313

Please sign in to comment.