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
81 changes: 81 additions & 0 deletions .github/workflows/apps-event-queue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Apps / Event Queue

on:
pull_request:
branches: ["*"]
paths:
- apps/event-queue/**
- packages/logger/**
- packages/events/**
- .github/workflows/apps-event-queue.yaml
- pnpm-lock.yaml
push:
branches: ["main"]
paths:
- apps/event-queue/**
- packages/logger/**
- packages/events/**
- .github/workflows/apps-event-queue.yaml
- pnpm-lock.yaml

jobs:
build:
runs-on: ubuntu-latest

permissions:
contents: read
id-token: write

strategy:
matrix:
platform: [linux/amd64]

steps:
- uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Check if Docker Hub secrets are available
run: |
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
echo "DOCKERHUB_LOGIN=false" >> $GITHUB_ENV
else
echo "DOCKERHUB_LOGIN=true" >> $GITHUB_ENV
fi

- name: Login to Docker Hub
uses: docker/login-action@v3
if: env.DOCKERHUB_LOGIN == 'true'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ctrlplane/event-queue
tags: |
type=sha,format=short,prefix=

- name: Build
uses: docker/build-push-action@v6
if: github.ref != 'refs/heads/main'
with:
push: false
file: apps/event-queue/Dockerfile
platforms: ${{ matrix.platform }}
tags: ${{ steps.meta.outputs.tags }}

- name: Build and Push
uses: docker/build-push-action@v6
if: github.ref == 'refs/heads/main' && env.DOCKERHUB_LOGIN == 'true'
with:
push: true
file: apps/event-queue/Dockerfile
platforms: ${{ matrix.platform }}
tags: ${{ steps.meta.outputs.tags }}
55 changes: 55 additions & 0 deletions apps/event-queue/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# https://github.com/WhiskeySockets/Baileys/issues/1003#issuecomment-2306467419
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine

RUN apk add --no-cache libc6-compat python3 make g++ curl

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

RUN npm install -g turbo
RUN npm install -g corepack@latest
RUN corepack enable pnpm

WORKDIR /app

COPY .gitignore .gitignore
COPY turbo.json turbo.json
RUN pnpm add -g turbo

COPY package.json package.json
COPY .npmrc .npmrc
COPY pnpm-*.yaml .

Comment on lines +20 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Do not bake .npmrc into the image; mount it as a BuildKit secret during install

Copying .npmrc into the image risks leaking tokens in layers. Use a secret mount for pnpm install instead.

-COPY .npmrc .npmrc
@@
-RUN pnpm install --frozen-lockfile
+RUN --mount=type=secret,id=npmrc,dst=/root/.npmrc pnpm install --frozen-lockfile

Follow-up (workflow): pass the secret to docker/build-push-action (see my workflow comment adding secrets: npmrc=${{ secrets.NPMRC }}).

Also applies to: 33-33

COPY tooling/prettier/package.json ./tooling/prettier/package.json
COPY tooling/eslint/package.json ./tooling/eslint/package.json
COPY tooling/typescript/package.json ./tooling/typescript/package.json

COPY packages/logger/package.json ./packages/logger/package.json
COPY packages/events/package.json ./packages/events/package.json
COPY packages/secrets/package.json ./packages/secrets/package.json
COPY packages/validators/package.json ./packages/validators/package.json
COPY packages/db/package.json ./packages/db/package.json
COPY packages/job-dispatch/package.json ./packages/job-dispatch/package.json
COPY packages/rule-engine/package.json ./packages/rule-engine/package.json

COPY apps/event-queue/package.json ./apps/event-queue/package.json

RUN pnpm install --frozen-lockfile

COPY . .

RUN turbo build --filter=...@ctrlplane/event-queue

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
USER nodejs

Comment on lines +44 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Alpine adduser/addgroup flags; current build will fail

BusyBox on Alpine does not support the long options --system/--gid/--uid as used; this will break the build. Use -S, -g, -u, and ensure ownership before switching users.

-RUN addgroup --system --gid 1001 nodejs
-RUN adduser --system --uid 1001 nodejs
-USER nodejs
+RUN addgroup -S -g 1001 nodejs \
+ && adduser -S -G nodejs -u 1001 nodejs \
+ && chown -R 1001:1001 /app
+USER nodejs
🤖 Prompt for AI Agents
In apps/event-queue/Dockerfile around lines 39 to 42, the addgroup/adduser
commands use non‑existent long BusyBox flags (--system/--gid/--uid) which break
Alpine builds; replace them with Alpine/BusyBox flags (addgroup -S -g 1001
nodejs and adduser -S -u 1001 -G nodejs nodejs), ensure the user is added to the
group, fix ownership of relevant app directories (chown -R nodejs:nodejs
<paths>) before switching users, and then keep the final USER nodejs line.

ENV NODE_ENV=production
ENV NODE_MAX_OLD_SPACE_SIZE=4096
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

NODE_MAX_OLD_SPACE_SIZE has no effect; use NODE_OPTIONS.

Node reads memory flags from NODE_OPTIONS or CLI args. The current env var won’t change heap size.

Apply:

-ENV NODE_MAX_OLD_SPACE_SIZE=4096
+ENV NODE_OPTIONS="--max-old-space-size=4096"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ENV NODE_MAX_OLD_SPACE_SIZE=4096
ENV NODE_OPTIONS="--max-old-space-size=4096"
🤖 Prompt for AI Agents
In apps/event-queue/Dockerfile around lines 49-49, the Dockerfile sets ENV
NODE_MAX_OLD_SPACE_SIZE=4096 which Node ignores; replace it with an ENV that
sets NODE_OPTIONS to include the memory flag (e.g. ENV
NODE_OPTIONS="--max-old-space-size=4096"), or if NODE_OPTIONS may already exist,
append the flag to the existing value (preserve any existing NODE_OPTIONS and
add --max-old-space-size=4096).


EXPOSE 3123

WORKDIR apps/event-queue/dist/

CMD ["node", "index.js"]
9 changes: 9 additions & 0 deletions apps/event-queue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Event Queue

This is a simple event queue that listens to the `ctrlplane-events` topic and logs the events to the console.

## Running

```bash
pnpm dev
```
8 changes: 8 additions & 0 deletions apps/event-queue/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import baseConfig, { requireJsSuffix } from "@ctrlplane/eslint-config/base";

/** @type {import('typescript-eslint').Config} */
export default [
{ ignores: [".nitro/**", ".output/**", "dist/**"] },
...requireJsSuffix,
...baseConfig,
];
33 changes: 33 additions & 0 deletions apps/event-queue/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@ctrlplane/event-queue",
"private": true,
"type": "module",
"scripts": {
"clean": "rm -rf .turbo node_modules",
"dev": "pnpm with-env tsx watch --clear-screen=false src/index.ts",
"lint": "eslint",
"build": "tsc",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env --"
},
"dependencies": {
"@ctrlplane/events": "workspace:*",
"@ctrlplane/logger": "workspace:*",
"@t3-oss/env-core": "catalog:",
"dotenv": "^16.4.5",
"kafkajs": "^2.2.4",
"zod": "catalog:"
},
"devDependencies": {
"@ctrlplane/eslint-config": "workspace:^",
"@ctrlplane/prettier-config": "workspace:^",
"@ctrlplane/tsconfig": "workspace:*",
"@types/node": "catalog:node22",
"eslint": "catalog:",
"prettier": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:"
},
"prettier": "@ctrlplane/prettier-config"
}
26 changes: 26 additions & 0 deletions apps/event-queue/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createEnv } from "@t3-oss/env-core";
import dotenv from "dotenv";
import { z } from "zod";

dotenv.config();

export const env = createEnv({
server: {
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
KAFKA_BROKERS: z
.string()
.default("localhost:9092")
.transform((val) =>
val
.split(",")
.map((s) => s.trim())
.filter(Boolean),
)
.refine((arr) => arr.length > 0, {
message: "KAFKA_BROKERS must be a non-empty list of brokers",
}),
},
runtimeEnv: process.env,
});
33 changes: 33 additions & 0 deletions apps/event-queue/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Kafka } from "kafkajs";

import { logger } from "@ctrlplane/logger";

import { env } from "./config.js";

const kafka = new Kafka({
clientId: "ctrlplane-events",
brokers: env.KAFKA_BROKERS,
});

const consumer = kafka.consumer({ groupId: "ctrlplane-events" });

export const start = async () => {
logger.info("Starting event queue", { brokers: env.KAFKA_BROKERS });
await consumer.connect();
await consumer.subscribe({ topic: "ctrlplane-events", fromBeginning: true });
logger.info("Subscribed to ctrlplane-events topic");

await consumer.run({
eachMessage: async ({ topic, partition, message }) => {
logger.info("Received event", {
topic,
partition,
message: message.value?.toString() ?? "No message",
});

return Promise.resolve();
},
});
};

start();
14 changes: 14 additions & 0 deletions apps/event-queue/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "@ctrlplane/tsconfig/base.json",
"compilerOptions": {
"target": "ESNext",
"outDir": "dist",
"noEmit": false,
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
"baseUrl": ".",
"esModuleInterop": true,
"importsNotUsedAsValues": "remove"
},
"include": ["src", "*.ts"],
"exclude": ["node_modules"]
}
2 changes: 1 addition & 1 deletion apps/event-worker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
USER nodejs

ENV NODE_ENV=production
ENV NODE_MAX_OLD_SPACE_SIZE=4096
ENV NODE_OPTIONS="--max-old-space-size=4096"

EXPOSE 3123

CMD node -r ./apps/event-worker/dist/instrumentation-node.js --max-old-space-size=${NODE_MAX_OLD_SPACE_SIZE} apps/event-worker/dist/index.js

Check warning on line 57 in apps/event-worker/Dockerfile

View workflow job for this annotation

GitHub Actions / build (linux/amd64)

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
58 changes: 49 additions & 9 deletions pnpm-lock.yaml

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

Loading