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
5 changes: 5 additions & 0 deletions .env.railway
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
REDIS_URL="${{Redis (Webhook).REDIS_URL}}" # Auto-configured Redis for webhook event storage
PORT="3000" # Server port for webhook endpoints
NODE_ENV="production" # Environment mode (keep as production for Railway)
TARGET_PLATFORM="" # Platform integration: "discord" or "telegram" - REQUIRED
UNTHREAD_WEBHOOK_SECRET="" # Webhook secret from Unthread dashboard - REQUIRED
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
${{ env.REGISTRY_GHCR }}:dev-${{ steps.meta.outputs.short_sha }}
labels: |
org.opencontainers.image.title=Unthread Webhook Server
org.opencontainers.image.description=Development build of Unthread Webhook Server
org.opencontainers.image.description=A reliable, production-ready Node.js server for processing Unthread.io webhooks with signature verification and smart platform handling.
org.opencontainers.image.version=dev-${{ steps.meta.outputs.short_sha }}
org.opencontainers.image.created=${{ steps.meta.outputs.build_date }}
org.opencontainers.image.revision=${{ github.sha }}
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ jobs:

# Combine all tags
ALL_TAGS="$DOCKERHUB_TAGS,$GHCR_TAGS"

echo "tags=$ALL_TAGS" >> $GITHUB_OUTPUT
echo "tags=$ALL_TAGS" >> $GITHUB_OUTPUT

- name: Build and push production images
uses: docker/build-push-action@v5
Expand All @@ -88,14 +87,14 @@ jobs:
tags: ${{ steps.tags.outputs.tags }}
labels: |
org.opencontainers.image.title=Unthread Webhook Server
org.opencontainers.image.description=A Node.js server application that receives webhook events from Unthread.io
org.opencontainers.image.description=A reliable, production-ready Node.js server for processing Unthread.io webhooks with signature verification and smart platform handling.
org.opencontainers.image.version=${{ steps.version.outputs.version }}
org.opencontainers.image.created=${{ steps.version.outputs.build_date }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.licenses=GPL-3.0
cache-from: type=gha
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run Trivy vulnerability scanner
Expand Down
150 changes: 148 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Any contributions are welcome, encouraged, and valued. See the following informa

## 📋 Code of Conduct

This project and everyone participating in it is governed by the project's Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to <opensource@wgtechlabs.com>.
This project and everyone participating in it is governed by the project's [Code of Conduct](./CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to <opensource@wgtechlabs.com>.

## 💖 How to Contribute

Expand All @@ -14,7 +14,7 @@ There are many ways to contribute to this open source project. Any contributions

If you can write code then create a pull request to this repo and I will review your code. Please consider submitting your pull request to the `dev` branch. I will auto reject if you submit your pull request to the `main` branch.

#### 🔧 Setup
#### 🔧 Development Setup

To get started with development:

Expand Down Expand Up @@ -44,6 +44,10 @@ To get started with development:
brew services start redis # macOS
sudo systemctl start redis-server # Linux
docker run -d -p 6379:6379 redis:alpine # Docker

# OR for full Docker setup with proper naming:
docker network create unthread-integration-network
docker-compose up -d redis-webhook
```

5. **Start the project in development mode**
Expand Down Expand Up @@ -97,11 +101,13 @@ src/
#### 🎯 Development Guidelines

- **TypeScript First**: All code must be written in TypeScript with strict type checking
- **Structured Logging**: Use `@wgtechlabs/log-engine` for all logging with built-in PII protection and security features
- **Error Handling**: Implement comprehensive error handling with detailed logging
- **Package Manager**: Use Yarn exclusively (enforced via preinstall script)
- **Code Style**: Follow existing patterns and maintain consistency
- **Environment**: Use Node.js 20+ for development
- **Redis Integration**: Ensure Redis connectivity for all webhook-related features
- **Webhook Integration**: Ensure compatibility with [`wgtechlabs/unthread-telegram-bot`](https://github.com/wgtechlabs/unthread-telegram-bot)

#### 🧪 Testing Guidelines

Expand Down Expand Up @@ -153,11 +159,151 @@ For other bugs, please create an issue with:
### 💡 Feature Requests

We welcome suggestions for new features! Please create an issue with:

- Clear description of the feature
- Use case and benefits
- Any implementation considerations
- Examples or mockups if applicable

## 📊 Advanced Logging Security with Log Engine

This project uses [`@wgtechlabs/log-engine`](https://github.com/wgtechlabs/log-engine) for enterprise-grade logging with built-in security features and comprehensive PII protection.

### 🔒 **Automatic Security Features**

**Zero Configuration PII Protection:**

- **Automatic Redaction**: Passwords, tokens, emails, API keys, and 50+ sensitive patterns are automatically protected
- **Deep Object Scanning**: Recursively scans nested objects and arrays for sensitive data
- **Content Truncation**: Large payloads are automatically truncated to prevent log bloat
- **Environment-Based Control**: Security automatically adapts based on NODE_ENV settings

**Built-in Patterns Protected:**

- **Authentication**: `password`, `token`, `apiKey`, `secret`, `jwt`, `auth`, `sessionId`
- **Personal Info**: `email`, `phone`, `ssn`, `firstName`, `lastName`, `address`
- **Financial**: `creditCard`, `cvv`, `bankAccount`, `routingNumber`
- **System**: `clientSecret`, `privateKey`, `webhookSecret`, `unthreadSecret`

### 🛡️ **Advanced Security Configuration**

**Custom Enterprise Protection:**

```javascript
import { LogEngine } from '@wgtechlabs/log-engine';

// Add custom patterns for enterprise-specific data
LogEngine.addCustomRedactionPatterns([
/internal.*/i, // Matches any field starting with "internal"
/company.*/i, // Matches any field starting with "company"
/webhook.*/i, // Matches webhook-specific fields
/unthread.*/i // Matches unthread-specific fields
]);

// Add dynamic sensitive field names
LogEngine.addSensitiveFields([
'webhookSecret',
'unthreadWebhookSecret',
'unthreadApiKey',
'redisPassword'
]);
```

**Secure Logging Examples:**

```javascript
// ✅ Automatic protection - no configuration needed
LogEngine.info('Webhook authentication', {
webhookId: '123456789', // ✅ Visible
webhookSecret: 'secret123', // ❌ [REDACTED]
targetPlatform: 'telegram', // ✅ Visible
unthreadApiKey: 'key_123' // ❌ [REDACTED]
});

// ✅ Event processing protection
LogEngine.info('Event processing', {
eventType: 'message_created', // ✅ Visible
eventId: 'evt_001', // ✅ Visible
signature: 'sha256=...', // ❌ [REDACTED]
payload: { /* large data */ } // Automatically truncated
});

// ✅ Redis queue security
LogEngine.info('Queue publishing', {
queueName: 'unthread-events', // ✅ Visible
platform: 'unthread', // ✅ Visible
redisUrl: 'redis://localhost', // ❌ [REDACTED]
eventCount: 5 // ✅ Visible
});
```

### ⚙️ **Environment Configuration**

**Production Security (Recommended):**

```bash
NODE_ENV=production # Full PII protection enabled
LOG_REDACTION_TEXT="[SECURE]" # Custom redaction text
LOG_MAX_CONTENT_LENGTH=150 # Truncate large content
```

**Development Debugging:**

```bash
NODE_ENV=development # Redaction disabled for debugging
LOG_REDACTION_DISABLED=true # Explicit disable
DEBUG_FULL_PAYLOADS=true # Show complete data
```

**Custom Security Configuration:**

```bash
# Custom sensitive fields (comma-separated)
LOG_SENSITIVE_FIELDS="webhookSecret,unthreadSecret,redisPassword"

# Custom redaction patterns (JSON array)
LOG_CUSTOM_PATTERNS='["/internal.*/i", "/company.*/i"]'

# Truncation settings
LOG_MAX_CONTENT_LENGTH=200
LOG_TRUNCATION_TEXT="... [CONFIDENTIAL_TRUNCATED]"
```

### 🔧 **Development & Debugging**

**Raw Logging for Development:**

```javascript
// ⚠️ Use with caution - bypasses all redaction
LogEngine.debugRaw('Full webhook payload', {
password: 'visible', // ⚠️ Visible (not redacted)
apiKey: 'full-key-visible' // ⚠️ Visible (not redacted)
});

// Temporary redaction bypass
LogEngine.withoutRedaction().info('Debug mode', sensitiveData);

// Test field redaction
const isRedacted = LogEngine.testFieldRedaction('webhookSecret'); // true
const currentConfig = LogEngine.getRedactionConfig();
```

### 📊 **Logging Benefits for This Webhook Server**

**Security Compliance:**

- **GDPR Ready**: Automatic PII protection for European compliance
- **Data Minimization**: Only necessary data is logged
- **Audit Trails**: Complete security event logging with timestamps
- **Incident Response**: Quick identification of security events

**Operational Benefits:**

- **Color-Coded Output**: Easy visual identification of log levels (🔵 INFO, 🟡 WARN, 🔴 ERROR)
- **Structured Logging**: Consistent format across all webhook components
- **Performance Optimized**: Minimal overhead with intelligent processing
- **TypeScript Support**: Full type safety and IDE integration

---

💻 with ❤️ by [Waren Gonzaga](https://warengonzaga.com), [WG Technology Labs](https://wgtechlabs.com), and [Him](https://www.youtube.com/watch?v=HHrxS4diLew&t=44s) 🙏
127 changes: 85 additions & 42 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,51 +1,93 @@
# Multi-stage build for Unthread Webhook Server

# Build stage
FROM node:20-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package manager files
COPY package.json yarn.lock .yarnrc ./
# =============================================================================
# UNTHREAD WEBHOOK SERVER - DOCKERFILE
# =============================================================================
# Multi-stage Docker build for the Unthread Webhook Server
#
# Build stages:
# 1. base - Base Alpine image with Node.js and security updates
# 2. deps - Install production dependencies only
# 3. build - Install dev dependencies and build the application
# 4. final - Create minimal runtime image with built app
#
# Usage:
# docker build -t unthread-webhook-server .
# docker run --env-file .env unthread-webhook-server
#
# =============================================================================

# syntax=docker/dockerfile:1

# Use Node.js 22.16 LTS Alpine with security patches
ARG NODE_VERSION=22.16-alpine3.21

# =============================================================================
# STAGE 1: Base Image
# =============================================================================
# Alpine Linux 3.21 base for minimal image size with latest security updates
FROM node:${NODE_VERSION} AS base

# Install security updates for Alpine packages
RUN apk update && apk upgrade && \
apk add --no-cache dumb-init && \
rm -rf /var/cache/apk/*

# Set working directory for all subsequent stages
WORKDIR /usr/src/app

# =============================================================================
# STAGE 2: Production Dependencies
# =============================================================================
# Install only production dependencies for runtime
FROM base AS deps

# Use bind mounts and cache for faster builds
# Downloads dependencies without copying package files into the layer
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=yarn.lock,target=yarn.lock \
--mount=type=cache,target=/root/.yarn \
yarn install --production --frozen-lockfile

# =============================================================================
# STAGE 3: Build Application
# =============================================================================
# Install dev dependencies and build the TypeScript application
FROM deps AS build

# Install all dependencies (including devDependencies for building)
RUN yarn install --frozen-lockfile --ignore-scripts

# Copy source code and build configuration
COPY src/ ./src/
COPY tsconfig.json ./

# Build the TypeScript application
RUN yarn build

# Production stage
FROM node:20-alpine AS production

# Set working directory
WORKDIR /app

# Copy package manager files
COPY package.json yarn.lock .yarnrc ./
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=yarn.lock,target=yarn.lock \
--mount=type=cache,target=/root/.yarn \
yarn install --frozen-lockfile

# Copy source code and build the application
COPY . .
RUN yarn run build

# =============================================================================
# STAGE 4: Final Runtime Image
# =============================================================================
# Minimal production image with only necessary files
FROM base AS final

# Set production environment with security options
ENV NODE_ENV=production \
NODE_OPTIONS="--enable-source-maps --max-old-space-size=512"

# Create a dedicated user for the application
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 -G nodejs

# Set environment to production
ENV NODE_ENV=production
# Install only production dependencies
RUN yarn install --frozen-lockfile --production --ignore-scripts && \
yarn cache clean
# Copy package.json for package manager commands
COPY --chown=nodejs:nodejs package.json .

# Copy built application from builder stage
COPY --from=builder /app/dist ./dist
# Copy production dependencies and built application
COPY --from=deps --chown=nodejs:nodejs /usr/src/app/node_modules ./node_modules
COPY --from=build --chown=nodejs:nodejs /usr/src/app/dist ./dist

# Copy environment example (for reference)
COPY .env.example ./

# Create non-root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
COPY --chown=nodejs:nodejs .env.example ./

# Change ownership of the app directory to nodejs user
RUN chown -R nodejs:nodejs /app
# Switch to non-root user
USER nodejs

# Expose the port the app runs on
Expand All @@ -55,5 +97,6 @@ EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"

# Start the application
# Use dumb-init for proper signal handling and start the application
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/app.js"]
Loading